Basicsの概念では、関数を定義する2つの方法を紹介しました。
最も一般的なのは、複数行にわたる定義です。
function muladd(x, y, z)
x * y + z
end
短い定義のための「代入形式」、または「1行形式」です。
muladd(x, y, z) = x * y + z
さらに短い3つ目の形式では、短くて一度きりの関数を、名前を付けずに作れます。
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
スラープは、関数を_定義する_ときに、任意の個数の個々の値を1つのコレクションにまとめるために使います。
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
呼び出しでは、どのキーワード引数でも使えます。 どのキーワードに反応し、どれを無視するかを決めるのは、関数の定義側です。