Elixirの勉強の続き( 前回の記事 )。今日は関数とモジュールについてです。

関数

無名関数

無名関数はfnとendでくくって表現します。

iex(1)> add = fn (x) -> x * x end
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(2)> add.(2)
4
iex(3)> add.(3)
9

無名関数を使うときは、関数を束縛した変数と引数の間に . が必要な点に注意が必要です。忘れちゃいそうですね。

無名関数を変数に束縛していますが、この変数の型は関数になります。

iex(1)> add = fn (x) -> x * x end
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(2)> is_function(add)
true

それから無名関数はクロージャです。関数定義時のコンテキストを保持します。

iex(1)> pi = 3.14159
3.14159
iex(2)> circle = fn (r) -> r * r * pi end
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(3)> circle.(2)
12.56636

当然ですが、関数内で束縛された変数のスコープは関数の中だけになります。

iex(1)> pi = 3.14159
3.14159
iex(2)> circle = fn (r) ->
...(2)> pi = 3.14
...(2)> g = 9.8
...(2)> r * r * pi
...(2)> end
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(3)> circle.(2)
12.56
iex(4)> pi
3.14159
iex(5)> g
** (CompileError) iex:5: undefined function g/0

キャプチャ構文

無名関数はもっとシンプルな書き方があります。

iex(1)> circle = &(&1 * &1 * 3.14)
#Function<6.54118792/1 in :erl_eval.expr/5>
iex(2)> circle.(2)
12.56

最初に登場した & はキャプチャ構文と呼ばれるものです。名前付き関数を関数として取り出すときに使用しますが(後で説明します)、上のように無名関数を定義するときのショートカットにも使えるのです。 カッコの中の &1 はひとつ目の引数を意味します。引数1 * 引数1 * 3.14 を実行するわけです。引数がふたつ以上あるなら、 &2, &3.... と表せます。

名前付き関数がいままでにもいくつか出て来ました。関数かどうかを調べるときに is_functionを使っていましたね。この関数を無名関数のように変数に束縛したいときにはどうすればいいでしょうか。

iex(1)> x = is_function
** (CompileError) iex:1: undefined function is_function/0

これではダメです。 is_function の返り値を変数 x に束縛するときの書き方です。 is_function/0 が定義されてないよ、というエラーメッセージが出ています。Elixirでは関数を表現するときに 関数名/引数の数 という書き方をします。 is_function は引数がひとつなので、 is_function/1 になります。

このような名前のついた関数を、実行するのではなく関数として取り出したいときにキャプチャ構文を使用します。

iex(1)> x = &is_function/1
&:erlang.is_function/1
iex(2)> x.(1)
false
iex(3)> x.(fn x -> x end)
true

& につづいて関数を表す 関数名/引数の数 という書き方をすると、その関数自体を取り出すことができます。こうやって取り出した関数を使用するときは、無名関数と同じく、引数との間に . が必要となります。

モジュール

定義

モジュールとは複数の関数をまとめたものです。モジュールを定義するには defmodule を、モジュール内の関数を定義するには def を使用します。 iex でのモジュール定義は見にくいのでわけて書きますが、そのままiexに投入することもできます。

defmodule Tsuka do
  def name do
    "Oishi"
  end
end
iex(1)> Tsuka.name
"Oishi"

プライベート関数

defp を使うプライベート関数を定義できます。プライベート関数はモジュール内でだけ使用できます。

defmodule Tsuka do
  def name do
    family_name
  end

  defp family_name do
    "Oishi"
  end
end
iex(1)> Tsuka.name
"Oishi"
iex(2)> Tsuka.family_name
** (UndefinedFunctionError) undefined function: Tsuka.family_name/0
    Tsuka.family_name()

デフォルト引数

名前付き関数はデフォルト引数を定義できます。 \\ を使用します。

defmodule Tsuka do            
  def name(prefix \\ "Oishi") do
    prefix <> "Tsukasa"           
  end
end
iex(1)> Tsuka.name
"OishiTsukasa"
iex(2)> Tsuka.name("OISHI")
"OISHITsukasa"