Julia 関数

関数は、1つのタスクを実行するための文の集まりです。

Juliaでは、関数は引数値からなるタプルを戻り値にマッピングするオブジェクトです。

Juliaでは、function関数を定義する基本的な構文は次のとおりです:

function functionname(args)
   expression
   expression
   expression
   ...
   expression
end

デフォルトでは、関数が返す値は最後に評価された式の値です。したがって、上記にはreturnステートメントはありません。もちろん、returnキーワードを使用すると、関数は即座に戻り値を返します:

例

julia> function f(x,y)
           x + y
       end
f (generic function with 1 method)
julia> f(2,3)
5

julia> function bills(money)
      if money < 0
         return false
      else
         return true
      end
   end
bills (generic function with 1 method)

julia> bills(50)
true

julia> bills(-50)
false

関数が複数の値を返す必要がある場合は、タプルを使用できます:

例

julia> function mul(x,y)
                  x+y, x*y
               end
mul (generic function with 1 method)

julia> mul(5, 10)
(15, 50)

関数内に式が1つだけの場合、functionキーワードを省略できます。等号の左側に関数名とパラメータを設定し、右側に式を設定します。これは代入の形式に似ています:

julia> f(x,y) = x + y
f (generic function with 1 method)

括弧がない場合、式fは関数オブジェクトを指し、他の値と同様に渡すことができます:

julia> g = f;
julia> g(2,3)
5

例

julia> f(a) = a * a
f (generic function with 1 method)

julia> f(5)
25

julia> func(x, y) = sqrt(x^2 + y^2)
func (generic function with 1 method)

julia> func(5, 4)
6.4031242374328485

変数名と同様に、Unicode文字も関数名として使用できます:

julia> ∑(x,y) = x + y
∑ (generic function with 1 method)

julia> ∑(2, 3)
5

戻り値の型

関数の戻り値の型を指定するには、::演算子を使用できます。

例

julia> function g(x, y)::Int8
           return x * y
       end;

julia> typeof(g(1, 2))
Int8

上記の関数の例では、x と y の型を無視して、Int8 型の値を返します。

オプション引数

関数内でパラメータにデフォルト値を設定できます。これにより、そのパラメータが指定されていない場合でも、デフォルト値を使用して計算できます:

次の例では、関数 pos を定義し、3つのパラメータを設定しています。そのうちパラメータ cz のデフォルト値は 0 に設定されており、関数呼び出し時にこのパラメータを指定しなくても構いません:

例

julia> function pos(ax, by, cz=0)
         println("$ax, $by, $cz")
      end
pos (generic function with 2 methods)

julia> pos(10, 30)
10, 30, 0

julia> pos(10, 30, 50)
10, 30, 50

キーワード引数

時には、定義した関数が多くのパラメータを必要とすることがありますが、これらの関数の呼び出しは、パラメータの順序を忘れてしまう可能性があるため、非常に面倒な場合があります。 例えば:

function foo(a, b, c, d, e, f)
...
end

パラメータの順序を忘れてしまい、以下のような関数呼び出しが発生する可能性があります:

foo("25", -5.6987, "hello", 56, good, 'ABC')
或
foo("hello", 56, "25", -5.6987, 'ABC', good)

これは非常に混乱しているように見えます。

Julia のキーワード引数を使用すると、位置だけでなく名前によってパラメータを識別できるため、これらの複雑な関数を簡単に使用および拡張できます。

キーワードを使用してパラメータをマークするには、関数のマークされていないパラメータの後にセミコロン;を使用し、その後ろに1つ以上のキーワードと値のペアkey=valueを続けます。次のようになります:

例

julia> function foo(a, b ; c = 10, d = "hi")
         println("a is $a")
         println("b is $b")
         return "c => $c, d => $d"
      end
foo (generic function with 1 method)

julia> foo(100,20)
a is 100
b is 20
"c => 10, d => hi"

julia> foo("Hello", "サンプル", c=pi, d=22//7)
a is Hello
b is サンプル
"c => π, d => 22//7"

キーワード引数を使用すると、パラメータの位置はそれほど重要ではなくなります。上記の関数は次のように呼び出すこともできます:

例

julia> foo(c=pi, d =22/7, "Hello", "サンプル")
a is Hello
b is サンプル
"c => π, d => 3.142857142857143"

匿名関数

匿名関数とは、関数名を持たない関数です。

匿名関数はプログラムの実行時に動的に宣言され、関数名がないことを除けば、標準の関数と同じです。

Julia では、匿名関数は map() やリスト内包表記など、多くの場所で使用できます。

匿名関数を使用すると、コードがより簡潔になります。

匿名関数の構文では記号->。

例

julia> x -> x^2 + 2x - 1
#1 (generic function with 1 method)

julia> function (x)
           x^2 + 2x - 1
       end
#3 (generic function with 1 method)

上記の例では、1つのパラメータxを受け取り、現在の値の多項式x^2+2x-1を返す関数を作成しています。

匿名関数の主な用途は、関数を引数として受け取る関数に渡すことです。典型的な例は map で、配列の各要素に関数を1回適用し、結果の値を含む新しい配列を返します:

例

julia> map(round, [1.2, 3.5, 1.7])
3-element Vector{Float64}:
 1.0
 4.0
 2.0

map の最初の引数として渡す変換関数がすでに存在する場合は、関数名を直接使用しても問題ありません。しかし、通常は使用する関数がまだ定義されていないため、匿名関数を使用する方が便利です:

例

julia> map(x -> x^2 + 2x - 1, [1, 3, -1])
3-element Vector{Int64}:
  2
 14
 -2

複数のパラメータを受け取る匿名関数は、構文 (x,y,z)->2x+y-z を使用して記述できます。一方、引数なしの匿名関数は ()->3 と記述します。引数なしの関数のこのような記述方法は奇妙に見えるかもしれませんが、遅延計算には必要です。この使用方法では、コードブロックを引数なしの関数にラップし、後でそれを f として呼び出します。

例えば、get の呼び出しを考えてみましょう:

例

get(dict, key) do
    # default value calculated here
    time()
end

上記のコードは、コードを含む匿名関数を使用して get を呼び出すことと同等です。これは do と end の間に囲まれており、次のようになります:

get(()->time(), dict, key)

ここでの time の呼び出しは、それをラップした引数なしの匿名関数によって遅延されます。この匿名関数は、dict に要求されたキーがない場合にのみ呼び出されます。


関数のネストと再帰

Julia では、関数はネストして使用できます。

次の例では、add() 関数内に関数 add1 をネストしています:

例

julia> function add(x)
         Y = x * 2
         function add1(Y)
           Y += 1
         end
         add1(Y)
       end
add (generic function with 1 method)

julia> d = 10
10

julia> add(d)
21

同様に、Julia の関数は再帰にすることもできます。

再帰とは、関数の定義内で関数自身を使用する方法のことです。

例を挙げます:
昔々、ある山がありました。山の中にはお寺があり、お寺には老和尚がいて、小和尚に物語を語っていました。その物語とは?「昔々、ある山がありました。山の中にはお寺があり、お寺には老和尚がいて、小和尚に物語を語っていました。その物語とは?『昔々、ある山がありました。山の中にはお寺があり、お寺には老和尚がいて、小和尚に物語を語っていました。その物語とは?……』」」」

次に、三項演算子を使用して再帰をテストします。三項演算子は3つのオペランドを操作し、expr ? a : bexpr が true の場合は a の評価結果の値、それ以外の場合は b の評価結果の値です。

例

julia> sum(x) = x > 1 ? sum(x-1) + x : x
sum (generic function with 1 method)

julia> sum(10)
55

上記の例は、ある整数までのすべての数の合計を、その数を含めて計算するために使用されます。この再帰では、x が 1 の場合に値が返されるという基本ケースが存在します。

再帰の最も有名な例は、n 番目のフィボナッチ数を計算することです。フィボナッチ数列とは、1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368........ という数列です。

この数列は第3項から始まり、各項は前の2つの項の和に等しくなります。

例

julia> fib(x) = x < 2 ? x : fib(x-1) + fib(x-2)
fib (generic function with 1 method)

julia> fib(10)
55

julia> fib(20)
6765

Map

Map の定義形式は次のとおりです:

map(func, coll)

ここで、func は関数であり、コレクション coll の各要素に順番に適用されます。Map は通常、匿名関数を含み、新しいコレクションを返します。

例

julia> map(A -> A^3 + 3A - 3, [10,3,-2])
3-element Array{Int64,1}:
 1027
   33
  -17

Filter

Filter の定義形式は次のとおりです:

filter(function, collection)

filter 関数は、その関数を呼び出した結果が false となる要素を削除した、コレクションのコピーを返します。

例

julia> array = Int[1,2,3]
3-element Array{Int64,1}:
 1
 2
 3
 
julia> filter(x -> x % 2 == 0, array)
1-element Array{Int64,1}:
 2
その他の拡張