Basics 개념에서는 함수를 정의하는 두 가지 방법을 소개했어요.
가장 흔히 쓰는 것은 여러 줄 형식이에요:
function muladd(x, y, z)
x * y + z
end
짧은 정의를 위한 "할당" 형식, 또는 "한 줄" 형식도 있어요:
muladd(x, y, z) = x * y + z
세 번째로, 훨씬 더 짧은 형식에서는 한 번만 쓰는 짧은 함수를 이름 없이 만들 수 있어요:
julia> map(x -> 2x, 1:3)
4-element Vector{Int64}:
2
4
6
julia> map((x, y) -> x * y, 1:3, 4:6)
3-element Vector{Int64}:
4
10
18
이때 x -> 2x는 "익명 함수"예요.
다른 언어에서 "람다 함수"라고 부르는 것과 같은 개념이에요.
인자가 여러 개일 때는 (x, y) -> x * y처럼 괄호가 필요하다는 점에 주의해요.
익명 함수는 Julia 코드에서 흔히 볼 수 있어요. 특히 map()이나 filter() 같은 고차 함수와 함께 쓸 때 자주 등장해요.
지금까지는 인자의 개수가 정확히 정해져 있고, 함수를 호출할 때 올바른 순서로 모든 인자를 넘겨야 하는 함수만 살펴봤어요. 이러면 제약이 많고 불편하겠죠. 그래서 다른 방법도 여러 가지가 있어요.
많은 언어처럼 Julia에서는 함수 정의에서 개별 인자에 기본값을 지정할 수 있어요.
그러면 함수를 호출할 때 그 인자에 값을 넘기거나, 생략하고 기본값을 사용할 수 있어요.
julia> f(x, y=10) = x * y
f (generic function with 2 methods)
julia> f(2, 3)
6
julia> f(2)
20
기본값이 없는 인자는 기본값이 있는 인자보다 앞에 와야 해요. 따라서 f(x=2, y)는 올바르지 않아요.
지금까지 살펴본 예제는 모두 positional arguments를 사용했어요. 함수를 호출할 때 넘기는 값이 함수 정의에 있는 해당 인자의 순서와 일치해야 하는 방식이에요.
많은 언어처럼 Julia는 keyword arguments도 지원해요.
함수를 호출할 때는 인자 이름을 반드시 지정해야 하지만, 여러 키워드 인자를 순서에 상관없이 지정할 수 있어요.
Julia의 독특한 점은, 함수 정의에서 키워드 인자가 있다면 그 앞에 세미콜론 ;을 붙여 위치 인자와 구분해야 한다는 거예요.
함수를 호출할 때는 마지막 위치 인자와 첫 번째 키워드 인자 사이에 ;나 , 중 아무거나 써도 돼요.
julia> b(x; y) = x + y
b (generic function with 1 method)
julia> b(2, y=3)
5
# keyword is required when calling
julia> b(2, 3)
ERROR: MethodError: no method matching b(::Int64, ::Int64)
The function `b` exists, but no method is defined for this combination of argument types.
위치 인자와 마찬가지로 기본값도 지정할 수 있어요.
매개변수와 이름이 같은 변수를 미리 계산해 둔 경우, 함수 호출에서 myarg=myarg 같은 문법을 쓰게 되는 일이 흔해요.
이런 경우에는 축약 문법을 쓸 수 있어요:
julia> width = 4.0
4.0
julia> height = √ width
2.0
julia> area(; width, height) = width * height
area (generic function with 1 method)
# repetition
julia> area(; width=width, height=height)
8.0
# shorthand form
julia> area(; width, height)
8.0
혹시 궁금했을까 봐 말하자면, 이 둘은 Julia 문법에서 유용하게 쓰이는 기능의 표준 명칭이에요.
둘 다 ... 연산자를 가리켜요.
스플랫은 함수를 _호출_할 때 컬렉션을 펼쳐서 함수가 필요로 하는 개별 값으로 만들어 주는 데 사용해요.
설명보다 예제로 보여드리는 게 더 쉬울 거예요:
julia> fxyz(x, y, z) = x * y * z
fxyz (generic function with 1 method)
julia> xyz = [2, 3, 4]
3-element Vector{Int64}:
2
3
4
# Using the vector directly in a function call is invalid
julia> fxyz(xyz)
ERROR: MethodError: no method matching fxyz(::Vector{Int64})
The function `fxyz` exists, but no method is defined for this combination of argument types.
# splatting converts the vector to 3 numbers, used as positional arguments
julia> fxyz(xyz...)
24
일부 "함수 호출"은 편의 문법에 가려져 있어서, 스플랫을 덜 뻔한 방식으로도 쓸 수 있어요.
예를 들어, 다중 할당은 내부적으로 튜플 생성 함수를 사용해요:
julia> first, rest... = [1, 2, 3, 4]
4-element Vector{Int64}:
1
2
3
4
julia> first
1
julia> rest
3-element Vector{Int64}:
2
3
4
키워드 인자도 스플랫으로 넘길 수 있는데, 보통 named tuple을 사용해요.
Dict도 되지만, 키는 심볼이어야 해요 (문자열은 여기서 동작하지 않아요).
# function with 3 keyword arguments
julia> fabc(; a, b, c) = a + b + c
fabc (generic function with 1 method)
# named tuple
julia> abc_nt = (a=2, b=3, c=4)
(a = 2, b = 3, c = 4)
# there are no positional arguments, so need to use ; before kw argument
julia> fabc(;abc_nt...)
9
# Dict
julia> abc_dict = Dict(:a=>2, :b=>3, :c=>4)
Dict{Symbol, Int64} with 3 entries:
:a => 2
:b => 3
:c => 4
julia> fabc(;abc_dict...)
9
슬러프는 함수를 _정의_할 때, 임의의 개수에 해당하는 개별 값들을 하나의 컬렉션으로 묶는 데 사용해요.
julia> f_more(i, j, more...) = i + j + sum(more)
f_more (generic function with 1 method)
julia> f_more(1, 3, 5, 7, 9, 11)
36
슬러프된 인자의 이름(여기서는 more)은 중요하지 않아요.
이 변수의 타입은 컴파일러가 정하지만, 위치 인자의 경우 보통 tuple이나 그와 비슷한 타입이 돼요.
키워드 인자도 슬러프할 수 있는데, 그러면 Dict(또는 비슷한 것)가 돼요.
julia> f_kwslurp(x, y; switches...) = :mult in keys(switches) ? x * y : x + y
f_kwslurp (generic function with 1 method)
julia> f_kwslurp(5, 6; mult=true)
30
julia> f_kwslurp(5, 6)
11
호출할 때 어떤 키워드 인자든 사용할 수 있어요. 어떤 키워드에 반응하고 어떤 것을 무시할지는 함수 정의에서 정해요.