いろいろ発見メモ

さまざまな試行と実験と発見と調べ物のメモ書きです

Elixir の匿名関数 (2)

引数のパターンマッチ

引数のパターンマッチで実際の処理を選ぶことができる。

次のような匿名関数を定義して…

iex> handle_open = fn
...> {:ok, file} -> "Read data: #{IO.read(file, :line)}"
...> {_, error} -> "Error: #{:file.format_error(error)}"
...> end

これで、以下の実行をしてみる。

iex> handle_open.(File.open("nofile"))
"Error: no such file or directory"
iex> handle_open.(File.open("test.txt"))
"Read data: abc def ghi\n"

File.open() の戻り値に対するパターンマッチで実行が振り分けられていることがわかる。(ところで上の :file は組込の Erlang のファイルモジュールを参照している。対して File.open は Elixir の File モジュールを参照しているということ。Erlang と Elixir のモジュールが混在できることは有利な反面、混乱もしそう)。

演習問題の解答

# Functions-2

f2 = fn
0,0,_ -> "FizzBuzz"
0,_,_ -> "Fizz"
_,0,_ -> "Buzz"
_,_,x -> x
end
IO.puts f2.(0,0,10)
IO.puts f2.(0,10,10)
IO.puts f2.(10,0,10)
IO.puts f2.(5,5,10)

# Functions-3

f2 = fn
0,0,_ -> "FizzBuzz"
0,_,_ -> "Fizz"
_,0,_ -> "Buzz"
_,_,x -> x
end
fizz_buzz = fn
n -> f2.(rem(n,3),rem(n,5),n)
end
IO.puts fizz_buzz.(10)
IO.puts fizz_buzz.(11)
IO.puts fizz_buzz.(12)
IO.puts fizz_buzz.(13)
IO.puts fizz_buzz.(14)
IO.puts fizz_buzz.(15)
IO.puts fizz_buzz.(16)

 

関数を返す関数 

# n を与えると、「引数に n を加えた数を返す関数」を返す関数 add_n を定義する

iex> add_n = fn n -> fn x -> x + n end end
#Function<6.90072148/1 in :erl_eval.expr/5>

# add_n に n として 5 を与えてみる。すると「引数に 5 を加えて返す関数」が得られた筈
iex> add5 = add_n.(5)
#Function<6.90072148/1 in :erl_eval.expr/5>

# 11 を引数にして呼んでみる。確かに 11 に 5 が足されている。
iex> add5.(11)
16

これはクロージャと呼ばれます。

演習問題

# Functions-4

iex> prefix = fn p -> fn str -> "#{p} #{str}" end end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex> prefix.("Mr.").("Dale")
"Mr. Dale"

引数に関数を渡す

先に作った fizz_buzz に、引数のリストから一つずつ値を取り出して与える。(いわゆる map 関数)

iex> Enum.map [10,11,12,13,14,15,16], fizz_buzz
["Buzz", 11, "Fizz", 13, 14, "FizzBuzz", 16]

&記法

短いヘルパー関数を定義して与えることは頻繁に行われるので Elixir にはそれを簡単に書くための構文が用意されています。

例えば以下の 2 行は同じ意味です。

sub3 = fn n -> n - 3 end

sub3 = &(&1 - 3)

最初の & が無名関数の定義を表しています。次の &1 は位置で参照される引数で、複数の引数がある場合には &1, &2, &3 ... という形で参照されていきます。

square = &(&1 * &1)

これは平方の定義例。

ここまでは匿名関数の話でした。次の章は名前付き関数の話。