モジュールとコンパイル,スクリプト

cf. 8 モジュール – Modules – Elixir

モジュール

Elixir では関数をモジュールの中に入れてグループ化する。モジュールは defmodule マクロで定義し、その中で def マクロを使って関数を定義する。またマクロって単語が出てきたけど気にしない。

iex(1)> defmodule Math do
...(1)>   def sum(a, b) do
...(1)>     a + b
...(1)>   end
...(1)> end
{:module, Math,
 <<70, 79, 82, 49, 0, 0, 4, 200, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 157,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:sum, 2}}
iex(2)> Math.sum(1, 2)
3

モジュールの関数を呼び出すときは「モジュール名.関数名」だな。

コンパイル

モジュールをファイルに書いて保存しておき、コンパイルすると再利用しやすくなる。例えば次のようなファイルを作る。拡張子は ex。

defmodule Math do

  def sum(a, b) do
    a + b
  end

end

このソースファイルは elixirc コマンドで Erlang VM のバイトコードにコンパイルできる。

^o^ > elixirc math.ex

^o^ > ls
Elixir.Math.beam  hello.exs  math.ex

Elixir.Math.beam っていうファイルが、コンパイルされたバイトコード。ここで iex を立ち上げると、Math モジュールが使えるようになる。カレントディレクトリのバイトコードは自動的に読み込まれるってわけだ。

^o^ > iex
Eshell V8.0  (abort with ^G)
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> Math.sum(1, 2)
3

スクリプトモード

コンパイルを必要としないのがスクリプトモード。こないだの記事で Hello, world プログラムを作ったのがこれ。こっちは拡張子を exs にする。ex ファイルと exs ファイルは中身的には区別はなく、違うのは目的。ex はコンパイルされるのを意図しているのに対して、exs はコンパイルせずにスクリプトモードで使用する。

defmodule Math do

  def sum(a, b) do
    a + b
  end

end

IO.puts Math.sum(1, 2)

上のスクリプト math.exs を実行すると次のように出力される。おっと、さっき作った Elixir.Math.beam を消しておくのを忘れないように。そうでないと「Math モジュールを再定義しようとしてる」とかいう意味(たぶん)の warning が出る。

^o^ > rm Elixir.Math.beam

^o^ > elixir math.exs
3

Elixir スクリプトの実行は elixir コマンドね。

名前付き関数

モジュールの中で、def/2 を使って関数を定義するのは上でもやった。この関数はモジュールの外からも呼べる関数になる。一方、defp/2 を使って定義するとモジュールの外からは呼べないプライベート関数になる。これらは名前付き関数と呼ばれる、たぶん、文脈からすると。

defmodule Math do

  def sum(a, b) do
    a + b
  end

  defp do_sum(a, b) do
    a + b
  end

end

IO.puts Math.sum(1, 2)
IO.puts Math.do_sum(1, 2)

上のスクリプトでは、関数 sum とプライベート関数 do_sum を定義して、呼び出している。これを実行すると:

^o^ > elixir math2.exs
warning: function do_sum/2 is unused
  math2.exs:6

3
** (UndefinedFunctionError) function Math.do_sum/2 is undefined or private
    Math.do_sum(1, 2)
    math2.exs:13: (file)
    (elixir) lib/code.ex:363: Code.require_file/2

こうなる。
Elixir の関数宣言(宣言て書いてあるな)は、ガードと複数句に対応している。例えばこんなふうに:

defmodule Math do

  def zero?(0) do
    true
  end

  def zero?(x) when is_number(x) do
    false
  end

end

IO.puts Math.zero?(0)
IO.puts Math.zero?(1)
^o^ > elixir math3.exs
true
false

関数のキャプチャ

関数を値として取り出すことをキャプチャと言い、& を頭につける。math.exs を iex で使ってみよう。

^o^ > iex math.exs
Eshell V8.0  (abort with ^G)
3
Interactive Elixir (1.3.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> fun = &Math.sum/2
&Math.sum/2
iex(2)> fun.(1, 3)
4

キャプチャ構文は関数を作るときのショートカットとしても使える。この場合 &1 は1番目の引数を意味する。

iex(3)> fun = &(&1 + 1)
#Function<6.52032458/1 in :erl_eval.expr/5>
iex(4)> fun.(2)
3

デフォルト引数

名前付き関数はデフォルト値を持つ引数をとることができる。

defmodule Concat do

  def join(a, b, sep \\ " ") do
    a <> sep <> b
  end

end

IO.puts Concat.join("hello", "world")
IO.puts Concat.join("hello", "world", "-")
^o^ > elixir concat.exs
hello world
hello-world

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください