Julia/Functions
Juliaでは、関数は値のタプルからまた別の値へとマッピングするオブジェクトである
Juliaの関数は純粋ではない
破壊していい
副作用あっていい
基本的な構文
code:jl
julia> function fac(n)
if n == 1
1
else
n * fac(n-1)
end
end
fac (generic function with 1 method)
julia> fac(5)
120
簡潔な構文
code:julia
julia> fac(n) =
if n == 1
1
else
n * fac(n-1)
end
fac (generic function with 1 method)
julia> fac(5)
120
関数はカッコで呼び出せるが、カッコをつけなければ関数オブジェクトそのものを指す
code:jl
julia> fac
fac (generic function with 1 method)
Unicode使える: ∑(x, y) = x + y
Juliaでは、引数は共有渡しである
つまり、仮引数名に実引数が再バインディングされたように振る舞う
同じものを指す
ゆえに、配列などのミュータブルなものを渡したら変更できる
code:jl
julia> rev(xs, tmp) =
if xs == []
tmp
else
end
rev (generic function with 1 method)
julia> rev(xs) = rev(xs, [])
rev (generic function with 2 methods)
5-element Vector{Any}:
5
4
3
2
1
引数に::型で型指定ができる
fib(n::Integer) = n ≤ 2 ? one(n) : fib(n-1) + fib(n-2)
これによるパフォーマンス上のインパクトは通常はない
JITでspecializedされたネイティブコードが一度生成され、それが使い回されるから
early return できる
ちなみに返り値の型指定はJulia文化ではあまりやらないとのこと
Juliaでは、何も返さないときにはnothingを返すのがしきたり
ScalaにおけるUnit
code:jl
julia> println(42)
42
julia> ans
julia> ans == nothing
true
匿名関数
x -> x * 2 のように書く
Tuple
()
(1,)
(1, 2, 3)
Named Tuple
(x = 42, y = 666)
ans.xでアクセス可能
ans[:x]も同様
Destructuring bindings
code:jl
5-element Vector{Int64}:
1
2
3
4
5
julia> y
2
右辺はイテレータでさえあればよい
関数もタプルを返せる
code:jl
julia> check(x) =
if x == 42
"ok", nothing
else
nothing, "error!"
end
check (generic function with 1 method)
julia> check(0)
(nothing, "error!")
julia> check(42)
("ok", nothing)
Goぽいですな
いらない値は_で捨てられる
code:jl
julia> _, err = check(0)
(nothing, "error!")
X[1] = ...のように配列や行列の要素に受けることができる
...で可変長に受けることができる
code:jl
5-element Vector{Int64}:
1
2
3
4
5
julia> head
1
julia> tail
4-element Vector{Int64}:
2
3
4
5
Named Tupleの場合は(; 名前...)として同名の引数で受けられる
code:jl
julia> check(x) =
if x == 42
(result="ok", error=nothing)
else
(result=nothing, error="error!")
end
check (generic function with 1 method)
julia> (; error) = check(0)
(result = nothing, error = "error!")
julia> error
"error!"
Varargs
f(x, y...) =としてvarargsできる
呼ぶ側でも使える
default args
普通に使える
kw args
仮引数リストで;を書いてそこ以降に名前を書くと普通に使える
code:jl
julia> greet(name; honorific="san") = println("Hello, $name $honorific !")
greet (generic function with 2 methods)
julia> greet("windymelt", honorific="kun")
Hello, windymelt kun !
blocks
begin .. endはブロック式
Schemeと同じ
Scalaと同じ
do .. endは 「第一引数にこの匿名関数を渡す」
code:jl
5-element Vector{Int64}:
1
2
3
4
5
julia> map(xs) do x
x * 2
end
5-element Vector{Int64}:
2
4
6
8
10
map(x -> x * 2, xs)と同じ
LISPの流儀を感じる
第一引数に関数を取るところが
それをブロックに持っていく構文を持ってるのがかなり賢い
Rubyも同じかな
関数合成
(f ∘ g)(x)でf(g(x))
\circ<TAB>で入力できる
code:jl
julia> map(reverse ∘ uppercase, split("hello world"))
2-element Vector{String}:
"OLLEH"
"DLROW"
pipeline operator
code:jl
julia> "hello world" |> split |> xs -> (uppercase).(xs)
2-element Vector{String}:
"HELLO"
"WORLD"
.|>もある
code:jl
2-element Vector{String}:
"HELLO"
"dlrow"
dot syntax
f(x)のかわりにf.(xs)のようにドットつきで呼び出すと全要素に適用される
1つの式でチェインして使ってもシングルループに展開される
similarを使うと同じシェイプの配列や行列を確保してくれるので、インプレースでやりたいときに活用できる