Elixir 始めた
まだ始めて間もないですが、気づいた点を記します。
文中のカッコ内の数字
(p.56)など。
書籍「プログラミングElixir 」(第2版)でのページ数を記載しています。
環境
GalliumOS(Linux) 3.0 on Chromebook Edger(Acer CB3-431)
Elixir 1.10.3 on docker
コメント (p.35)
#
のあとに書きます。pythonと同じ。
関数
名前付き関数 (p.53)
モジュールの中で定義します。モジュール名は大文字で始めます。関数名は小文字から始めます。
defmodule Basic do # モジュール定義
def sky(first) do # 名前付き関数定義
first |> Enum.map(fn x -> x + 10 end)
end
def allindex(list, value) do
0..length(list)-1 |> Enum.filter(fn x -> Enum.at(list, x) == value end)
end
end
名前付き関数の呼び出し方
モジュール名から始めます。引数をカッコ内に書きます。
iex> Basic.sky([3, 5, 7, 9])
[13, 15, 17, 19]
無名関数 (p.41)
名前付き関数内などで定義されます。
(1) 引数を元に処理をする
関数定義
iex> type = fn x, y -> x * y end
(2) 引数でパターンマッチングする(p.43)
関数定義
type = fn
_, 0 -> "excpetion!"
x, y -> x * y
end
無名関数の呼び出し
引数は関数名の後にドットをつけてから書きます。
引数が無い場合でも呼び出し時のカッコは省略不可です。
iex> type.(3, 5)
15
iex> type.(7, 0)
"exception!"
無名関数での注意点
無名関数へのパイプ演算子
第一引数に渡されます。
iex> func = fn list -> length(list) end
iex> [7, 9, 5, 0] |> func.()
4
無名関数で回帰は書けない
無名関数は自分自身を呼べ出せないようです。
回帰を記述にするには名前付き関数を使用します。
iex> func = fn
...> 9, sum -> sum
...> n, sum -> func.(n+1, sum + n)
...> end
iex> func.(1, 0) # iexでの関数定義ではエラーはでませんが、呼び出すとエラーとなります。
関数を引数として渡す(p.47)
基本的に関数を引数として渡すことができるのは無名関数だけで、名前付き関数はそのままでは渡せません。名前付き関数を渡す場合は、無名関数にかぶせて渡します。
iex> apply = fn {a, b, c}, func -> func(a, b, c) end # 名前付き関数では未定義でエラー
iex> apply = fn {a, b, c}, func -> func.(a, b, c) end # 無名関数ならOK
iex> ({x, y, z}
...> |> apply.(&func1/3)
...> |> apply.(&func2/3)
...> )
...> # func1/3, func2/3とも名前付き関数、引数:3個、返り値:タプル/要素3個 とする
...> # もしくは
iex> ({x, y, z}
...> |> (fn {a, b, c} -> func1(a, b, c) end).()
...> |> (fn {a, b, c} -> func2(a, b, c) end).()
...> )
パターンマッチング後の処理を複数書く場合の書き方
with
を使わない場合は、do
やend
は不要で、つぎのパターンまでずらずらと記述するだけです。
iex> func = fn
...> :false, list, _ -> list
...> :true, list, n ->
...> key = Enum.at(list, n) # ずらずら
...> [key | list] # ずらずら
...> end
簡略化記法 (p.48)
&
を使います。&1
は1個目の引数を指します。
iex> Enum.map([1, 2, 3, 4, 5], &(&1 * 2) )
[2, 4, 6, 8, 10]
関数の返り値(名前付き関数,無名関数とも)
関数の処理の一番最後の部分に記述します。
パイプ演算子を用いる場合は最後の処理が終わった後のデータが返り値になります。
def main(s) do
:
処理
:
main関数の返り値
end
defmodule Spl do
def getkey(x, y) do
threestr = fn intg ->
intg |> Integer.to_string() |> String.pad_leading(3, "0") end
threestr.(x) <> threestr.(y) # 返り値
end
def main(s) do
s |> String.split() |> Enum.map(fn x -> String.to_integer(x) end) |> Enum.sum()
end
end
iex> Spl.getkey(15, 3)
"015003"
iex> Spl.main("6 13 25 34")
78
変数を保護を行いたい場合は、with式を使います。
回帰リストの基本的書き方 (p.71)
名前付き関数とリストで書きます。
呼び出し関数の最後の呼び出し部を最初に書きます。
def func1([]), do: 定数
def func1([head | tail]) do
func2(head) (演算子) func1(tail)
end
先日twitterに載っていたコラッツ予想の計算は以下のように書けました。
defmodule Main do
def main(1, acc), do: Enum.reverse([1 | acc])
def main(num, acc) when rem(num, 2) == 0, do: main(div(num, 2), [num | acc])
def main(num, acc), do: main(num * 3 + 1, [num | acc])
end
iex> Main.main(7, []) # accの初期値は必ず[]とする。
[7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
リスト (p.29, p.71)
[]に囲まれたデータ列。
リストの要素を得る(インデックス指定)
Enum.at/3
を使います。インデックスは0から始めます。
iex> Enum.at([1, 2, 3, 4, 5], 3)
4
リストのスライス
Enum.slice/3
を使います。
iex> Enum.slice([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2, 5) # index:2 から5個を切り出す
[3, 4, 5, 6, 7]
リストの要素の書き換え
List.replace_at/3
を使います。正しくは書き換えではなく、新しいリストの作成です。
iex> List.replace_at([1, 2, 3, 4, 5], 2, 9)
[1, 2, 9, 4, 5]
リストの要素の追加
List.insert_at/3
を使います。こちらも新しいリストの作成です。
iex> List.insert_at([1, 2, 3, 4, 5], 2, 17)
[1, 2, 17, 3, 4, 5]
数字のリストが文字リストで表示されないようにする (p.121)
:io.format/2
もしくは:io.fwrite/2
を用います。
両者は機能的にはほぼ同じのようです。
iex> [67, 65, 84] # 数字リストは文字リストで表示されることがある。
'CAT'
iex> :io.format("~w~n", [ [67, 65, 84] ])
[67,65,84]
iex> :io.fwrite("~w~n", ['CAT'])
[67,65,84]
フォーマットの意味
~w
: データをそのまま表示する。
~p
: データを整形して表示する。
~s
: 文字列を表示する。
~n
: 改行
タプル (p.28, p.84)
{}に囲まれたデータ列。関数呼び出しの返り値を得るのに使用されます。
タプルのデータの変更は高ストのため、頻繁な要素の入れ替えには向いていないようです。
タプルの要素を得る(インデックス指定)
要素数が少ない場合はパターンマッチングで取り出せます。
iex> {_, sec, _ } = {"first", "second", "third"}
{"first", "second", "third"}
iex> sec
"second"
Kernel.elem/2
も使えます。
iex> elem({"first", "second", "third"}, 1)
"second"
マップ (p.28)
まだ使いこなせてませんが、MapモジュールとEnumモジュールがあれば、大抵のことはできそうです。
マップの作成
Map.put/3
を用います。
iex> map = %{"NY" => "New York"}
iex> map = Map.put(%{}, "NY", "New York")
%{"NY" => "New York"}
マップの値の取り出し
Map.get/3
を使います。
iex> Map.get(map, "NY")
"New York"
マップの更新(p.87)
Map.put/3
で新しく値を入れることができます。
iex> Map.put(map, "NY", "NEW YORK")
%{"NY" => "NEW YORK"}
Map.update!/3
を使うと現在の値を使った表記ができます。
iex> mapc = Map.update!(map, "NY", fn value -> value <> " city" end)
%{"NY" => "New York city"}
文字列 (p.117)
"
で囲います。
文字列の要素を得る(インデックス指定)
String.at/2
を使います。
iex> String.at("Elixir", 3)
"x"
1文字ごとのリストの作成
String.graphemes/1
を使います。
iex> String.graphemes("Elixir")
["E", "l", "i", "x", "i", "r"]
文字列中の特定の文字の個数を数える
一例です。
iex> str = "what you have to do is to sleep now."
iex> str |> String.graphemes() |> Enum.count(fn x -> x == "a" end) # "a"の個数を数える
2
注意点
- whenガード節では使用できる関数が限られている。(p.58)
- パターンマッチング部では関数や演算子は使用できない。
関数や演算子を置きたい場合は、呼び出し側に置く。
func = fn
i, i -> "no change" # これは問題なし
i, i+1 -> "one by one" # i+1 部分がエラーとなる
_, _ -> "others"
end
iex> func.(head1, head2)
func1 = fn
i, i -> "no change"
_, _ -> "others"
end
func2 = fn
i, i -> "one by one"
_, _ -> "others"
end
iex> func1.(head1, head2)
iex> func2.(head1, head2 -1) # 呼び出し側の引数内で演算する
- ピン演算子はパターンマッチング部でしか使用できない。
競技プログラミングでの入力の受け方
1行の受け取り
IO.gets("")
もしくは IO.read(:line)
を使います。
入力文字列
7 18 9 3
iex> givendata = IO.read(:line) |> String.trim() |> String.split() |> Enum.map(&String.to_integer/1)
iex> givendata
[7, 18, 9, 3]
入力文字列
5
3
14
11
iex> givendata = for _ <- 1..4, do: IO.read(:line) |> String.trim() |> String.to_integer()
iex > givendata
[5, 3, 14, 11]
変数の値を出力 (p.117)
"#{variable}"
を使います。
iex> {x, y} = {"Romeo", "Julliet"}
iex> first = "#{x} et #{y}"
iex> first
"Romeo et Julliet"
以上
Discussion