個人的 Elixir関連メモ
個人的にElixirで便利だと思った機能やコード片などを載せていく。
タイトルはTipsとしたが、雑多なメモになっていくはず。
雑多なメモ書きが思ったより多かったためタイトル変更。
Elixirに関する気になったことならなんでも書いていきます。
iex(1)> 1..10 |> Enum.map(&Kernel.**(2, &1))
[2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
2の階乗を1~10まで求める。
四則演算はKernel
モジュールが持っている関数なので、&記法を使う場合はモジュール名を書く。
Elixirのドキュメントに書かれているKernelモジュールのGuardで利用可能な関数の件数と、
そのドキュメントの生成元のコードに書かれているGuardで利用可能な件数の数がズレているのはどうしてだろう
ドキュメント: 58件
ソースコード: 52件(ブラウザ上で "@doc guard: true" で検索)
for x <- , ["\a", "\b", "\d", "\e", "\f", "\n", "\r", "\s", "\t", "\v"] do: {x, Code.eval_string("?#{x}") |> elem(0) }
[
{"\a", 7},
{"\b", 8},
{"\d", 127},
{"\e", 27},
{"\f", 12},
{"\n", 10},
{"\r", 13},
{" ", 32},
{"\t", 9},
{"\v", 11}
]
エスケープ文字と各コードポイントの対応表。
空白文字を \s
で表現できるのを初めて知った。
?\s
の代わりに、?\
(半角空白)でもコードポイントが得られる。
def before_noon?(datetime) do
datetime
|> NaiveDateTime.to_time()
|> Time.compare(~T[12:00:00Z])
=== :lt
# 改行を無くすとこの形
# datetime |> NaiveDateTime.to_time() |> Time.compare(~T[12:00:00Z]) === :lt
end
えっ、これ動くの?と思ったコード。
でも、パイプライン演算子を1列に並べれば納得。
今後は関数の最後に結果を比較して返すようなコードは変数への代入せずに書ける。
Exercismの回答は書き方の参考になるものが多い。
&1.price
や Mapに対しての {_key, value}
という書き方がまだまだ思い付かない。
なぜElixirなのか?
客観的にElixirを使うべき情報が揃ってて良さそう。
あとでちゃんと読む。
目を通した。
どれも昔の情報しか無いから、サイト自体が古いのかな?
もっと大きいサービスでの活用事例があればいいんだけどねえ。
iex(1)> %{} = %{hoge: 100}
%{hoge: 100}
iex(2)> [] = [hoge: 100]
** (MatchError) no match of right hand side value: [hoge: 100]
(stdlib 4.3.1) erl_eval.erl:496: :erl_eval.expr/6
iex:91: (file)
式の左辺に %{}
を置いてもマップならマッチングしてしまう。
Listで似たような形で書くとエラーになるため、Mapは気をつけていないと意図しない挙動をしそう。
このElixir Forumでも話題になっていた。
ガード節で判定させるなら、when map == %{}
を使うのが良さそう。
iex(1)> file_types
%{
bmp: %{
binary_size: 2,
common_extension: "bmp",
media_type: "image/bmp",
signature: "BM"
}
}
iex(2)> file_types[:bmp][:signature]
"BM"
iex(3)> text = "BM"
"BM"
iex(4)> case text do
...(4)> file_types[:bmp][:signature] === text -> true
...(4)> _ -> false
...(4)> end
** (CompileError) iex:4: cannot invoke remote function Access.get/2 inside a match
(elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
(elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
(elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
(elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
(elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
(elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
(elixir 1.14.4) src/elixir_expand.erl:591: :elixir_expand.expand_args/3
case
の判定の中でMapデータの呼び出しは使えない。他のパターンマッチも同様。
パターンマッチの条件はコンパイル時に埋め込むので実行しないと値が分からないものは条件には使えない。
こういう場合はプロパティを使わなければならない。
Elixirだけの話ではないけれど、リスト内包表記は英語で"List Comprehensions"というらしい。
並行処理: AをしながらBをする、AもBもやる
並列処理: Aをやる人とBをやる人が居る
アクターモデルは、
CPUから見た時は並行処理(複数のプロセスを切り替えながら処理をする)だが、
アクター(?)から見ると並列処理(各プロセスは独立している)
となる
iex> calculator = &(&1) # fn x -> x end と同じ
#Function<42.3316493/1 in :erl_eval.expr/6>
iex> hoge = 10
10
iex> calculator.(hoge)
10
iex> Enum.map(1..3, &(calculator.(hoge)))
** (CompileError) iex:3: invalid args for &, expected one of:
* &Mod.fun/arity to capture a remote function, such as &Enum.map/2
* &fun/arity to capture a local or imported function, such as &is_atom/1
* &some_code(&1, ...) containing at least one argument as &1, such as &List.flatten(&1)
Got: calculator.(hoge)
(elixir 1.14.4) src/elixir_expand.erl:501: :elixir_expand.expand_fn_capture/4
(elixir 1.14.4) src/elixir_expand.erl:587: :elixir_expand.expand_arg/3
(elixir 1.14.4) src/elixir_expand.erl:603: :elixir_expand.mapfold/5
(elixir 1.14.4) src/elixir_expand.erl:867: :elixir_expand.expand_remote/8
(elixir 1.14.4) src/elixir.erl:376: :elixir.quoted_to_erl/4
(elixir 1.14.4) src/elixir.erl:277: :elixir.eval_forms/4
(elixir 1.14.4) lib/module/parallel_checker.ex:110: Module.ParallelChecker.verify/1
&記法を利用した中で無名関数の引数呼び出しをすると、呼び出し先の関数が分からなくなるらしい。
import, alias, require, useの比較
import | alias | require | use | |
---|---|---|---|---|
概要 | モジュールを読み込む | モジュール名を略記 | マクロを読み込む | __use__関数を呼び出す |
書き方 | import Module | alias Module | require Module | use Module |
公式ドキュメント