いろいろ発見メモ

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

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)

これは平方の定義例。

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

 

 

Elixir の匿名関数 (1)

書籍の第5章より

Elixir は関数型言語なので、関数も一級市民として扱われます。

この章では以下のトピックを扱っています

匿名関数 (anonymous function)

匿名関数は fn 引数リスト -> 関数本体 end という記述で定義します。

# 引数に 1 を足す匿名関数を add1 という変数に代入してみる
iex> add1 = fn x -> x + 1 end
# 匿名関数の呼び出し(変数名のの後ろに '.' が必要)
iex> add1.(3)
4
# 二つの引数の差をとる匿名関数を定義して sub2 に代入
iex> sub2 = fn x, y -> x - y end 
#Function<12.90072148/2 in :erl_eval.expr/5>
iex> sub2.(10,3)
7
# 引数がなくても呼び出しにカッコは必要。単に 11 を返す匿名関数
iex> eleven = fn -> 11 end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex> eleven
#Function<20.90072148/0 in :erl_eval.expr/5>
iex> eleven.()
11

 関数とパターンマッチ

Elixir では代入は存在しません。

a = 1 と書かれたものも代入ではなく、パターンマッチです。この場合 a が 1 に束縛されます。

関数呼び出しでも同様に、実引数が仮引数にパターンマッチで束縛されます。

# 引数を3つの要素のリストにする

iex> make3 = fn a -> [a, a, a] end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex> make3.(5)
[5, 5, 5]

演習問題

# ご注意。この先に演習問題(Your Turn)の解答を書きます

iex> list_concat = fn [e1,e2],[e3,e4] -> [e1,e2,e3,e4] end
#Function<12.90072148/2 in :erl_eval.expr/5>
iex> list_concat.([:a,:b],[:c,:d])
[:a, :b, :c, :d]

iex> sum = fn a,b,c -> a+b+c end
#Function<18.90072148/3 in :erl_eval.expr/5>
iex> sum.(1,2,3)
6

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

 

この項続きます。。。

Elixir のデータ型

 Elixir に関して。以下の本を読んでいます。

Programming Elixir: Functional |> Concurrent |> Pragmatic |> Fun

Programming Elixir: Functional |> Concurrent |> Pragmatic |> Fun

 

 1章はインストールなど、2章はパターンマッチ、そして3章はイミュータブルであることの意義と解説が簡単な例題を使って続き、いよいよ4章から言語の基礎解説が始まります。関数型言語を知っている人は2,3章を飛ばして読んでも大丈夫ですが、手続き型言語しか知らない人は2,3章を読んで少しでも雰囲気を掴んでおかないと頭の中が混乱することが確実です。

値に関係する型

システム型

  • PID とポート型
  • 参照型

コレクション型

  • タプル型
  • リスト型
  • マップ型
  • バイナリ型 

これ以外に関数も独自の型を持っていますが、その解説はあとの章で出てくるらしい。