プログラミングElixir第5章

第5章 無名関数

記述方法

fn parameter-list -> body end

関数とパターンマッチ

関数を定義してみます.

iex(1)> fn (a,b) -> a+b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(2)> add = fn (a,b) -> a+b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> add (1,2)
** (SyntaxError) iex:3: unexpected parentheses. If you are making a function call, do not insert spaces between the function name and the opening parentheses. Syntax error before: '('

iex(3)> add. (1,2)
3

練習問題

iex(2)> list_concat= fn a,b -> a ++ b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> list_concat.( [:a, :b], [:c, :d])
[:a, :b, :c, :d]
iex(4)> list_concat.( 2, 4)
** (ArgumentError) argument error
    :erlang.++(2, 4)

TODO: 関数の引数の型をマッチングさせる方法... → "ガード節" を使う。 when キーワードを

iex(1)> pair_tuple_to_list = fn {a,b} -> [a, b] end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> pair_tuple_to_list.( { 1234, 5678 } )
[1234, 5678]

関数中に複数のbody

引数がタプルの場合? マッチングにより実行する処理を変更できる。 C言語のif文とは違って、Elixirはパターンマッチにより処理を分岐する(雰囲気)。

fn
  {:ok, file} -> "Open success",
  {_, error}  -> "Error: #{:file.format_error(error)}"
end

練習問題

iex(1)> fb = fn
...(1)>  0, 0, _ -> "FizzBuzz"
...(1)>  0, _, _ -> "Fizz"
...(1)>  _, 0, _ -> "Buzz"
...(1)>  _, _, x -> x
...(1)> end
#Function<18.128620087/3 in :erl_eval.expr/5>
iex(2)> fb.(0,0,0)
"FizzBuzz"
iex(3)> fb.(0,0,1)
"FizzBuzz"
iex(4)> fb.(0,1,0)
"Fizz"
iex(5)> fb.(0,1,1)
"Fizz"
iex(6)> fb.(1,0,0)
"Buzz"
iex(7)> fb.(1,0,1)
"Buzz"
iex(8)> fb.(1,1,0)
0
iex(9)> fb.(1,1,1)
1


iex(14)> a=fn a -> fb.(rem(a,3),rem(a,5),a) end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(15)> a.(10)
"Buzz"
iex(16)> a.(11)
11
iex(17)> a.(12)
"Fizz"
iex(18)> a.(13)
13
iex(19)> a.(14)
14
iex(20)> a.(15)
"FizzBuzz"
iex(21)> a.(16)
16
iex(22)> a.(17)
17

関数を返す関数

fn...end は 関数を作って返す。変数に束縛(どっちがどっち?)されて、使用するときは ".()" で関数呼び出しになる...デリファレンス?

iex(1)> add_n = fn n -> (fn other -> n + other end) end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> add_three = add_n.(3)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(3)> add_six = add_n.(6)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(4)> add_three.(8)
11
iex(5)> add_six.(2)
8

上記の例のように、似たような関数を作る場合に便利が良い。外側の関数引数を、内側(返すほう)の body で参照すると、その値を使った関数が返される。

Cで関数をマクロで生成していたようなことをスマートにできるという感じ..か.

&記法

fn..end を省略すると &() とかける。タプルを返す関数は &{} 関数引数は &amp;n で記述し、n番目の引数は n=1,2,.. となる。

iex(1)> add_one = &(&1+1)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> add_one.(2)
3

iex(3)> add_one = fn n -> n+1 end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(4)> add_one.(2)
3

定義済み関数の名前なし関数が取得できる。

iex(4)> &length/1
&:erlang.length/1
iex(5)> &length/2
** (UndefinedFunctionError) function :erl_eval.length/2 is undefined or private
iex(4)> a=&length/1
iex(5)> a=&length/1
&:erlang.length/1
iex(6)> a.("hoge")
** (ArgumentError) argument error
    :erlang.length("hoge")
iex(6)> a.([1,2,2])
3

演習

iex(1)> Enum.map [1,2,3,4], fn x -> x + 2 end
[3, 4, 5, 6]

iex(2)> Enum.map [1,2,3,4], &(&1+2)
[3, 4, 5, 6]
iex(2)> Enum.each [1,2,3,4], fn x -> IO.inspect x end
1
2
3
4
:ok
iex(3)> Enum.each [1,2,3,4], &(IO.inspect &1)
1
2
3
4
:ok
iex(4)> Enum.each [1,2,3,4], &IO.inspect &1
1
2
3
4
:ok