関数定義
関数の定義はごく普通。キーワードはfunction で、その後にシグネチャ、複数の文、さいごにend。return で値を明示的に返すこともできるが、最後の式まで実行された場合にはその式の値が関数の返り値になる。
function f(x , y)
x + y
end
もっと簡単に定義することもできる。
f(x, y) = x + y
でよい。
無名関数
python で言うところのlambdaも書ける。書き方はとてもシンプルでコンパクトだ。
julia> f = (x, y) -> x + y
Pythonのlambdaは後ろに式がひとつだけしか書けないのでいろいろ不便だが、Juliaの場合はコードブロックでいくらでも書けるので、複雑なコールバック関数なんかにも使える。
julia> afunc = (x, y) -> begin
x + y
end
コードブロック
Juliaではコードブロックが値を持つ。関数型言語っぽい。ブロックはbegin endで囲んで複数の文を並べる。
julia> begin
x = 1
y = 2
x + y
end
3
カッコでくくって、;で区切る書き方もある。
julia> (x = 1; y = 2; x + y)
3
いずれの場合も最後の文の値が、ブロック全体の値になる。
可変数引数
任意個の引数をとる関数は仮引数の後ろに...
を用いて定義する。この記法はJavaと似ているがJavaでは同じ型でなければならない。Pythonでは*
を仮引数の前に書く。
julia> function fun(a, b, x...)
(a, typeof(a), b, typeof(b), x, typeof(x))
end
可変数引数の仮引数x
はTupleとなる。
julia> fun(1, 2, 3, 4)
(1, Int64, 2, Int64, (3, 4), Tuple{Int64,Int64})
引数展開
同じ...
を呼び出し側で用いると、配列、タプルなどを展開して関数に渡すことができる。先程の関数をタプルで呼び出す。Pythonでは*
引数の前につける。
julia> x = (2,3,4,5)
(2, 3, 4, 5)
julia> fun(x...)
(2, Int64, 3, Int64, (4, 5), Tuple{Int64,Int64})
省略可能引数
引数の一部を省略可能にし、省略値に対して省略した場合の値を設定する事ができる。これも、多くの言語にある機能だ。
julia> function fun(x, y=1)
x, y
end
fun (generic function with 3 methods)
julia> fun(2)
(2, 1)
julia> fun(2,3)
(2, 3)
キーワード引数
引数に変数名前を指定させるキーワード引数も使える。これもPythonにもある機能だが、JuliaのそれはPythonのものとはちょっと違う。Juliaでは通常の位置引数とキーワード引数が明確に分離されていて、`;'で区切って定義する必要がある。これに対してPythonでは、キーワード引数は通常デフォルト値を指定することでキーワード引数とするがよく考えると、デフォルト値がある、ということとキーワード引数であるということは、必ずしも一致しない。Juliaではこの混同が整理されている。
たとえば通常の引数x
とキーワード引数y
を定義するには
julia> function func(x; y)
(x, y)
end
のように定義する。y
はキーワード引数だが、デフォルト値は存在しない。この関数を呼び出すには、
julia> func(1, y = 2)
(1, 2)
julia> func(1; y = 2)
(1, 2)
のようにする。呼び出す場合にはカンマでもセミコロンでも構わない。
キーワード引数は、必ずキーワードを指定しなければならない。省略するとエラーになる。
julia> func(1, 2)
ERROR: MethodError: no method matching func(::Int64, ::Int64)
逆に、キーワード引数でないx
を明示するとやはりエラーとなる。
julia> func(x = 1, y = 2)
ERROR: MethodError: no method matching func(; x=1, y=2)
呼び出す際には、キーワードを先に書いても良い。
julia> func(y = 2, 1)
(1, 2)
キーワード引数の部分をキーワード変数名 = 値
と書くのではなく、シンボル => 値
のように書くこともできる。キーワード変数名が動的に定まるときに有効ということらしい。シンボルは':'をシンボル名の前につけるか、Symbol
関数を用いて作る。
julia> ysymbol = Symbol("y")
:y
julia> func(1; ys => 2)
(1, 2)
Pythonではディクショナリと**
演算子で、複数のキーワード引数を同時に指定する事ができるが、いまのところJuliaにはこれに相当する機能はなさそうだ、JuliaでもDictionaryを用いて同様のことができる。呼び出し時には、過変数引数と同じように...
を用いる。
julia> func(x; y, z) = (x, y, z)
func (generic function with 1 method)
julia> dict = Dict(:y => 2, :z => 3)
Dict{Symbol,Int64} with 2 entries:
:y => 2
:z => 3
julia> func(1; dict...)
(1, 2, 3)
この場合、呼び出し時の;
は,
では代用できない。
julia> func(1, dict...)
ERROR: MethodError: no method matching func(::Int64, ::Pair{Symbol,Int64}, ::Pair{Symbol,Int64})
Closure とローカル変数のキャプチャ
Juliaの関数はクロージャで外部のローカル変数をキャプチャする。次の関数は、ローカル変数c
を作り、このローカル変数を呼び出すたびにインクリメントする関数を返す。3行目が関数の定義で、これが関数定義の最終行なのでそのままgetCounter
の返り値になる。
julia> function getCounter()
c = 0
() -> (c += 1; c)
end
getCounter (generic function with 1 method)
この関数を呼び出すと内部に定義されたクロージャが返されるのだが、このクロージャは、ローカル変数のc
をキャプチャしているので、次のようにカウンタとしてつかうことができる。
julia> counter = getCounter()
#27 (generic function with 1 method)
julia> counter()
1
julia> counter()
2