プログラミング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
を用いる。同じ関数名で 混在することはできない(同じスコープにしないとマッチングが取れないよね)
パイプ演算子( |>
)
関数の返り値を、次の関数の第一引数に取るとき、パイプ演算子が使える。
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")