Julia Language
関数
サーチ…
構文
- f(n)= ...
- 関数f(n)... end
- n ::タイプ
- x - > ...
- f(n)do ... end
備考
最も一般的な汎用関数の他に、組み込み関数もあります。そのような関数にはis
、 isa
、 typeof
、 throw
および同様の関数が含まれます。組み込み関数は、通常、JuliaではなくC言語で実装されるため、ディスパッチの引数型に特化することはできません。
数字を四角にする
これは、関数を定義する最も簡単な構文です:
square(n) = n * n
関数を呼び出すには、丸括弧(間にスペースを入れないでください)を使用します。
julia> square(10)
100
関数はJuliaのオブジェクトであり、他のオブジェクトと同様にREPLに表示することができます。
julia> square
square (generic function with 1 method)
すべてのジュリア関数は、デフォルトで汎用(別名多形とも呼ばれます)です。私たちのsquare
関数は、浮動小数点値と同様に機能します:
julia> square(2.5)
6.25
...または行列さえ:
julia> square([2 4
2 1])
2×2 Array{Int64,2}:
12 12
6 9
再帰関数
単純再帰
再帰と三項条件付き演算子を使用して、組み込みfactorial
関数の代替実装を作成することができます。
myfactorial(n) = n == 0 ? 1 : n * myfactorial(n - 1)
使用法:
julia> myfactorial(10)
3628800
木を使った作業
再帰関数は、多くの場合、データ構造、特にツリーデータ構造に最も役立ちます。 Juliaの式は木構造であるため、再プログラミングはメタプログラミングにとって非常に便利です。たとえば、以下の関数は、式で使用されるすべてのヘッドの集合を集めます。
heads(ex::Expr) = reduce(∪, Set((ex.head,)), (heads(a) for a in ex.args))
heads(::Any) = Set{Symbol}()
私たちの機能が意図したとおりに機能していることを確認できます:
julia> heads(:(7 + 4x > 1 > A[0]))
Set(Symbol[:comparison,:ref,:call])
この関数はコンパクトで、 高次関数のreduce
、データ型のSet
、ジェネレータ式など、さまざまな高度な手法を使用しています。
派遣入門
::
構文を使用して、引数の型をディスパッチできます。
describe(n::Integer) = "integer $n"
describe(n::AbstractFloat) = "floating point $n"
使用法:
julia> describe(10)
"integer 10"
julia> describe(1.0)
"floating point 1.0"
通常、静的複数ディスパッチまたは動的単一ディスパッチのいずれかを提供する多くの言語とは異なり、ジュリアは完全な動的複数ディスパッチ機能を備えています。つまり、複数の引数に対して関数を特殊化することができます。これは、特定の型の操作のための特別なメソッドと他の型の代替メソッドを定義するときに便利です。
describe(n::Integer, m::Integer) = "integers n=$n and m=$m"
describe(n, m::Integer) = "only m=$m is an integer"
describe(n::Integer, m) = "only n=$n is an integer"
使用法:
julia> describe(10, 'x')
"only n=10 is an integer"
julia> describe('x', 10)
"only m=10 is an integer"
julia> describe(10, 10)
"integers n=10 and m=10"
オプションの引数
Juliaは、関数がオプションの引数を取ることを可能にします。その背後には、マルチディスパッチの特別なケースとして実装されています。たとえば、一般的なFizzバズ問題を解いてみましょう。デフォルトでは1:10
の範囲の数値に対して行いますが、必要に応じて異なる値を設定します。また、 Fizz
やBuzz
さまざまなフレーズを使用できます。
function fizzbuzz(xs=1:10, fizz="Fizz", buzz="Buzz")
for i in xs
if i % 15 == 0
println(fizz, buzz)
elseif i % 3 == 0
println(fizz)
elseif i % 5 == 0
println(buzz)
else
println(i)
end
end
end
REPLでfizzbuzz
を調べると、4つの方法があると言われています。許可される引数の組み合わせごとに1つのメソッドが作成されました。
julia> fizzbuzz
fizzbuzz (generic function with 4 methods)
julia> methods(fizzbuzz)
# 4 methods for generic function "fizzbuzz":
fizzbuzz() at REPL[96]:2
fizzbuzz(xs) at REPL[96]:2
fizzbuzz(xs, fizz) at REPL[96]:2
fizzbuzz(xs, fizz, buzz) at REPL[96]:2
パラメータが指定されていないときにデフォルト値が使用されていることを確認できます。
julia> fizzbuzz()
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
オプションのパラメータは、提供する場合に受け入れられ、尊重されます。
julia> fizzbuzz(5:8, "fuzz", "bizz")
bizz
fuzz
7
8
パラメトリックディスパッチ
Vector{T}
やDict{K,V}
などのパラメトリック型で関数を送出する必要があることがよくありますが、型パラメータは固定されていません。この場合は、パラメトリックディスパッチを使用して処理できます。
julia> foo{T<:Number}(xs::Vector{T}) = @show xs .+ 1
foo (generic function with 1 method)
julia> foo(xs::Vector) = @show xs
foo (generic function with 2 methods)
julia> foo([1, 2, 3])
xs .+ 1 = [2,3,4]
3-element Array{Int64,1}:
2
3
4
julia> foo([1.0, 2.0, 3.0])
xs .+ 1 = [2.0,3.0,4.0]
3-element Array{Float64,1}:
2.0
3.0
4.0
julia> foo(["x", "y", "z"])
xs = String["x","y","z"]
3-element Array{String,1}:
"x"
"y"
"z"
単純にxs::Vector{Number}
書こうとするかもしれません。しかし、これは、型が明示的にVector{Number}
オブジェクトに対してのみ機能します。
julia> isa(Number[1, 2], Vector{Number})
true
julia> isa(Int[1, 2], Vector{Number})
false
これが原因であるパラメトリック不変物体Int[1, 2]
はない Vector{Number}
それだけ含めることができるので、 Int
一方、SをVector{Number}
数字の任意の種類を含有することができると予想されます。
汎用コードの記述
ディスパッチは非常に強力な機能ですが、タイプごとにコードを特化するのではなく、すべてのタイプで機能する汎用コードを記述する方がよい場合がよくあります。ジェネリックコードを書くことは、コードの重複を避ける。
たとえば、整数のベクトルの平方和を計算するコードを次に示します。
function sumsq(v::Vector{Int})
s = 0
for x in v
s += x ^ 2
end
s
end
しかし、このコードはInt
のベクトルに対してのみ機能します。 UnitRange
では動作しません:
julia> sumsq(1:10)
ERROR: MethodError: no method matching sumsq(::UnitRange{Int64})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
Vector{Float64}
では動作しません:
julia> sumsq([1.0, 2.0])
ERROR: MethodError: no method matching sumsq(::Array{Float64,1})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
このsumsq
関数を書く良い方法は
function sumsq(v::AbstractVector)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
これは、上記の2つのケースで機能します。しかし、ある意味ではベクトルではない四角形を合計したいかもしれないコレクションがあります。例えば、
julia> sumsq(take(countfrom(1), 100))
ERROR: MethodError: no method matching sumsq(::Base.Take{Base.Count{Int64}})
Closest candidates are:
sumsq(::Array{Int64,1}) at REPL[8]:2
sumsq(::AbstractArray{T,1}) at REPL[11]:2
私たちは怠惰な反復可能性の四角形を合計することはできないことを示しています。
もっと一般的な実装は、単純に
function sumsq(v)
s = zero(eltype(v))
for x in v
s += x ^ 2
end
s
end
どの場合でもうまくいく:
julia> sumsq(take(countfrom(1), 100))
338350
これは最も慣用的なジュリアコードであり、あらゆる状況に対応できます。他の言語では、型の注釈を削除するとパフォーマンスに影響する可能性がありますが、Juliaではそうではありません。パフォーマンスのためにタイプの安定性のみが重要です。
命令的階乗
長形式の構文は、複数行関数を定義するために使用できます。これは、ループなどの必須の構造を使用する場合に便利です。末尾の式が返されます。たとえば、以下の関数はfor
ループを使用して整数n
階乗を計算for
ます 。
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
fact
end
使用法:
julia> myfactorial(10)
3628800
より長い関数では、使用されるreturn
文を見るのが一般的です。 return
ステートメントは末尾には必要ではありませんが、それは明瞭にするために使用されることがあります。例えば、上記の関数を書く別の方法は、
function myfactorial(n)
fact = one(n)
for m in 1:n
fact *= m
end
return fact
end
これは上記の関数と同じ動作です。
匿名関数
矢印の構文
匿名関数は、 ->
構文を使用して作成できます。これは、関数をmap
関数などの高次関数に渡す場合に便利です。以下の関数は、 配列 A
各数値の2乗を計算します。
squareall(A) = map(x -> x ^ 2, A)
この関数の使用例:
julia> squareall(1:10)
10-element Array{Int64,1}:
1
4
9
16
25
36
49
64
81
100
複数行の構文
function
構文を使用して、複数の匿名関数を作成できます。たとえば、次の例では最初のn
数値の階乗を計算しますが、組み込みfactorial
ではなく無名関数を使用します。
julia> map(function (n)
product = one(n)
for i in 1:n
product *= i
end
product
end, 1:10)
10-element Array{Int64,1}:
1
2
6
24
120
720
5040
40320
362880
3628800
ブロック構文を実行する
関数への最初の引数として無名関数を渡すことは非常に一般的であるため、 do
ブロックの構文があります。構文
map(A) do x
x ^ 2
end
は
map(x -> x ^ 2, A)
多くの場合、前者の方がはっきりしている可能性があります。特に、無名関数で多くの計算が行われている場合にはそうです。 do
ブロック構文は、リソース管理上の理由からファイルの入出力に特に便利です。