プログラミングElixir第6章

モジュールと名前付き関数

名前付き関数を定義するには、その親になるモジュールを定義する必要がある。モジュールなくしては名前付き関数を定義できない。

FILE: times.exs

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

関数のボディ記述

以下の三種類すべて同じ記述方法。 "do: block" が基本で、あとはシンタックスシュガーぽい。"()"はブロックの記述か。

def double(n), do: n*2
def double(n), do
  n*2
end
def double(n), do: (
  n*2
)

練習問題

重複するので割愛。コンパイル方法:

  • iex 引数でファイルを渡す
  • iex>プロンプトから c "filename.exs"

関数呼び出しとパターンマッチ

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

iex内でも module定義できますね..

iex(8)> defmodule Factorial do
...(8)>   def of(0), do: 1
...(8)>   def of(n), do: n * of (n-1)
...(8)> end
{:module, Factorial,
 <<70, 79, 82, 49, 0, 0, 4, 60, 66, 69, 65, 77, 65, 116, 85, 56, 0, 0, 0, 130,
   0, 0, 0, 15, 16, 69, 108, 105, 120, 105, 114, 46, 70, 97, 99, 116, 111, 114,
   105, 97, 108, 8, 95, 95, 105, 110, 102, ...>>, {:of, 1}}
iex(9)> Factorial.of 3
6
iex(10)> Factorial.of 7
5040
iex(11)> Factorial.of 10
3628800

関数定義順にパターンマッチが試みられるようである。先食いするような記述はElixirが警告を出す(後に定義した関数が呼ばれることがない旨)。

ガード節

関数の引数をチェックする。パターンマッチでは型まで判定できないので、whenキーワードを使う:

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

ここでは 自然数であることを確認している。 is_*() で変数の型を知ることができる。タブ補完してみると、以下が見える:

iex(1)> is_
is_atom/1         is_binary/1       is_bitstring/1    is_boolean/1
is_float/1        is_function/1     is_function/2     is_integer/1
is_list/1         is_map/1          is_nil/1          is_number/1
is_pid/1          is_port/1         is_reference/1    is_tuple/1
Erlang/OTP 21 [erts-10.2.4] [source] [64-bit] [smp:48:48] [ds:48:48:10] [async-threads:1]
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)

デフォルトパラメータ

デフォルトパラメータは バックスラッシュ二つ( \ ) をパラメータ変数の後ろにおいて、値を続ける。

パラメータのマッチングは先頭から順次マッチングがとられる。必須パラメータのマッチングが行われ、不足する場合はエラーとなる。デフォルトパラメータが複数あり、呼び出し時に一部が渡される場合は、デフォルトパラメータが定義されている変数の先頭から順に適用される。

defmodule TestModule do
  def func( p1, p2 \\ 2, p3 \\ 3, p4 ), do: IO.inspect [p1,p2,p3,p4]
end
iex(4)> TestModule.func  11,22,33,44
[11, 22, 33, 44]
[11, 22, 33, 44]
iex(5)> TestModule.func  11,22,33
[11, 22, 3, 33]
[11, 22, 3, 33]
iex(6)> TestModule.func  11,22
[11, 2, 3, 22]
[11, 2, 3, 22]
iex(7)> TestModule.func  11

練習問題

iex(13)> c "guess.exs"
warning: redefining module TestModule (current version defined in memory)
  guess.exs:1

[TestModule]
iex(14)> TestModule.guess 273 , 1..1000
Is it 500
Is it 250
Is it 375
Is it 312
Is it 281
Is it 265
Is it 273
finish!
:ok

if文つかわずに... ということでちょっと型チェックが緩いけれどこんな感じ? FILE: guess.exs

defmodule TestModule do
  def get_range(a,m,s,_) when a<m, do: s..m
  def get_range(a,m,_,e) when a>m, do: m..e
  def get_range(a,_,_,_), do: a..a

  def guess(actual, s..e) when s<e, do: (
    m = div(s+e, 2)
    IO.puts "Is it #{m}"
    guess(actual, get_range(actual, m , s, e))
  )
  def guess(_,_), do: IO.puts "finish!"

end

プライベート関数

スコープがモジュールに収丸場合、 def ではなく、 defp を用いる。同じ関数名で 混在することはできない(同じスコープにしないとマッチングが取れないよね)

パイプ演算子( |&gt;

関数の返り値を、次の関数の第一引数に取るとき、パイプ演算子が使える。

func1 |> func2

モジュール

  • 名前空間を提供してくれるだけ。
  • 入れ子にできるが、内部的には一階層しかなく、モジュール名が長くなるだけ(Ex. "Module1.Module2"という名になる)

import ディレクティブ

モジュール名を引数にして import ModuleName と記述する。 ModuleName.func のモジュール名を記述せず、 func と書ける。

alias ディレクティブ

入れ子になったモジュール名を、任意の代替名で参照できる。

alias Module.My.Device, as: MyDev
alias Module.My.Device, as: Device
alias Module.My.Device

下の二つは同じ意味で、as:を省略すると、モジュール名の最後のドット以降をaliasとして宣言したことになる。

require ディレクティブ

モジュールで定義したマクロを使うときに、requireしておくといいらしい。

TODO: マクロの勉強

モジュールの属性

モジュール定義のスコープ内で、以下のように宣言する。モジュール内の関数から @name で参照できる。

@name value

属性 name の値 value, と読み, おおむねモジュールによって定まる定数や authorなどの文字列を記述するのが慣例のようだ.

モジュールの名前:Elixir, Erlang

モジュール名は、内部処理でアトム :"モジュール.サブモジュール...." と表現され、関数呼び出しには、アトム+ "." + モジュールの関数 として扱われる。

Erlangの関数を呼び出す場合は、Elixirのモジュール名がCamelCaseであるのと対照的に、小文字で始まる。これによりElixirとErlangのモジュールを区別しているようである.

Erlangの io モジュール, format関数を呼ぶ例:

:io.format("format is not equals to C lang.~n")