Tambourine作業メモ

主にスキル習得のためにやった作業のメモ。他人には基本的に無用のものです。

Elixirで遊んでみる(4)

6章は「モジュールと名前付き関数」である。

ここでやっと普通の関数定義の説明がある。

で、Rubyっぽいこんな感じだ。でも、Rubyならdoはいらない

defmodule Times do
  def double(n) do
    n * 2
  end
end

しかし、do...endはシンタックスシュガーで、do: (...)が基本的な構文らしい。

つまり、def はキーワード引数で:doにブロックを取るような関数だと。ほー・・・。

上の定義はこう書いてもいいらしい(が、「頼むからやめてくれ」と書いてあるw)

defmodule Times, do: (def double(n), do: n * 2)

なるほど。

関数の引数でパターンマッチしたい場合は、単に関数を複数書く。 例えば、階乗の計算はこんな感じ。上からマッチが試されるので、順番大事。

defmodule Factorial do
  def of(0), do: 1
  def of(n), do: n * of(n-1)
end

パターンマッチではなく、引数の型や値の評価で実行する関数を区別したい場合はガード節を付ける。こんな感じ。

defmodule Factorial do
  def of(0), do: 1
  def of(n) when is_integer(n) and n > 0 do
    n * of(n-1)
  end
  def of(_), do: :error
endend

OCamlと比べると、型によるパターンマッチが出来ないのは面倒くさく感じなくもない。 型それ自体が面倒くさいという意見もあると思うけど(笑)。

次に出てくるのは|>で書く、パイプライン。メソッドチェーンの関数言語版だ。 昔、Rubyにも提案されていて、trunkにサクッと入ってたことはあるけど、どうも上手く行かなかったらしい。

それはともかく、例えば、「1から10までの数字を2倍して、5より大きいもの」を表現するのに、パイプラインがないと

iex> Enum.filter(Enum.map(1..10, &(&1 * 2)), &(&1 > 5))   
[6, 8, 10, 12, 14, 16, 18, 20]

こうなっちゃうけど、パイプラインがあれば、

iex> (1..10) |> Enum.map(&(&1 * 2)) |> Enum.filter(&(&1 > 5))
[6, 8, 10, 12, 14, 16, 18, 20]

うん。明らかにこっちの方が見やすい。

ちなみにRubyでは

irb> (1..10).map{_1 * 2}.filter{_1 > 5}
=> [6, 8, 10, 12, 14, 16, 18, 20]

パイプラインの必要性がわからない。まあ、だから無くなったんだと思うけど。

後は、モジュールについて。属性の説明が面白い。