プログラミングElixir第5章
2020/06/19
第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
を省略すると &()
とかける。タプルを返す関数は &{}
関数引数は &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