Elixir入門
#Elixir
拡張子
.ex - アプリケーションコードはこっち. 最終的にバイトコードにコンパイルして実行します
.exs - テストコードやスクリプトはこっち
実行方法
$ elixir file.exs
code:sh
$ iex
iex(1)> c file.exs
ビルドツール
Mix
条件分岐
Elixirでの三項演算子の書き方
ピン演算子
変数の現在の値をパターンマッチのパターンとして使います
code:elixir
a = 100
^a = 100 # => 👌OK
^a = 200 # => 🙅NG
1, ^a, 3 = 1, 100, 3 # => 👌OK
1, ^a, 3 = 1, 200, 3 # => 🙅NG
key = "name"
%{ ^key => name } = %{ "name" => "hoge" } # => 👌OK (%{"name" => "hoge"})
データ構造
タプル
code:elixir
{ 1, 2 }
{ :ok, "hello" }
リスト
code:elixir
# 空リスト
[]
# パターンマッチ
a, b, c = 10, 20, 30
a # => 10
b # => 20
c # => 30
head | tail = 1,2,3,4,5
head # => 1
tail # => 2,3,4,5
# 連結
1,2 ++ 3,4,5 # => 1,2,3,4,5
# 要素の確認
1 in 1, 2, 3 # => true
"hello" in 10, 20 # => false
# Listモジュール
List.flatten(1,2], [[3, 4]) # => 1, 2, 3, 4
List.foldl(10, 20, 30, 0, fn x, sum -> sum + x end) # => 60
List.replace_at(1,2,3, 1, 100) # => 1, 100, 3
List.keyfind({:a, "hoge"}, {:b, "fuga"}, :a, 0) # => {:a, "hoge"}
List.keyfind({:foo, 1}, {:bar, 2}, {:baz, 3}, 2, 1) # => {:bar, 2}
List.keydelete({:foo, 1}, {:bar, 2}, {:baz, 3}, 2, 1) # -=> foo: 1, baz: 3
List.keyreplace({:foo, 1}, {:bar, 2}, {:baz, 3}, 2, 1, {:hoge, 20}) # => foo: 1, hoge: 20, baz: 3
キーワードリスト
code:elixir
# 記法
name: "hoge", age: 20 # => [ {:name, "hoge"}, {:age, 20} ]と同義
name: "hoge", height: 160:name # => "hoge"
# 最後の引数がキーワードリストの場合、ブラケットを省略できる
do_something arg, opt1: 1, opt2: "hello" # do_something arg, [opt1: 1, opt2: "hello"]と同義
# Keywordモジュール
Keyword.get(name: "hoge", age: 15, name: "piyo", :name) # => "hoge"
Keyword.get(a: 1, b: 2, :c, 3) # => 3
Keyword.get_values(name: "hoge", age: 80, name: "fuga", :name) # => "hoge", "fuga"
Keyword.merge(name: "hoge", age: 3, name: "piyo", height: 180) # => age: 3, name: "piyo", height: 180
マップ
code:elixir
map = %{ name: "piyo", age: 10 }
map:name # => "piyo"
map = %{ map | name: "hoge" } # => %{age: 10, name: "hoge"}
# パターンマッチ
%{ name: a_name } = %{ name: "hoge", age: 30 } # => %{age: 30, name: "hoge"}
a_name # => "hoge"
%{ name: _, age: _ } = %{ name: "hoge", age: 24 } # => %{age: 24, name: "hoge"}
%{ name: a_name } = %{ a: 1 } # => 🙅NG
%{ name: "hoge" } = %{ name: "hoge", age: 30 } # => OK
%{ name: "piyo" } = %{ name: "hoge", age: 30 } # => 🙅NG
# Mapモジュール
Map.keys %{ name: "hoge", age: 15 } # => :age, :name
Map.values %{ first_name: "hoge", last_name: "fuga" } # => "hoge", "piyo"
Map.drop %{ first_name: "hoge", last_name: "piyo", age: 10 }, :first_name, :last_name # => %{age: 10}
Map.put %{ name: "hoge" }, :likes, "Elixir" # => %{likes: "Elixir", name: "hoge"}
Map.has_key? %{ first_name: "hoge" }, :last_name # => false
Map.pop %{ name: "piyo", likes: "Deno" }, :likes # => {"Deno", %{name: "piyo"}}
Map.equal? %{ a: 1, b: 2 }, %{ a: 1, b: 2 } # => true
Map.put_new(%{ name: "hoge" }, :height, 180) # => %{height: 180, name: "hoge"}
セット
MapSetを使う
code:elixir
MapSet.new # => #MapSet<[]>
set = Enum.into(1..10, MapSet.new) # => #MapSet<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>
MapSet.member? set, 4 # => true
構造体
モジュール名が構造体の名前として扱われます
モジュール内でdefstructマクロを使用して構造を定義します
code:elixir
defmodule User do
defstruct name: "", age: 15
def to_string(%User{name: name, age: age}) do
"name: #{name}, age: #{age}"
end
end
code:elixir
%User{} # => %User{age: 15, name: ""}
%User{ name: "hoge" } # => %User{age: 15, name: "hoge"}
%User{ name: "hoge", no_such_key: true } # => 🙅NG (KeyError)
# フィールドへのアクセス
user = %User{}
user.age # => 15
# パターンマッチ
%User{ name: user_name } = user
user_name # => ""
# 更新
user = %User{ user | name: "piyo" } # => %User{age: 15, name: "piyo"}
User.to_string(user) # => "name: , age: 15"
コレクションの処理
code:elixir
Enum.map(1,2,3, &(&1 * 2)) # => 2, 4, 6
ストリーム
code:elixir
# Enumモジュールの関数にストリームを渡すと結果を得られる
Stream.map(1,2,3,4,5, &(&1*&1)) |> Enum.to_list # => 1, 4, 9, 16, 25
Stream.map(1,2,3,4,5, &(&1*&1)) |> Stream.map(&(&1*2)) |> Enum.to_list # => 2, 8, 18, 32, 50
# Stream.cycle => 列挙可能なデータから無限ストリームを生成する。最後の要素まで到達したら、最初の要素に戻る
Stream.cycle(1,2,3) |> Enum.take(5) # => 1, 2, 3, 1, 2
# Stream.repeatedly
Stream.repeatedly(fn -> 1 end) |> Enum.take(3) # => 1, 1, 1
# Stream.iterate
Stream.iterate(0, &(&1 + 1)) |> Enum.take(4) # => 0, 1, 2, 3
Stream.iterate(1, &(&1 * 2)) |> Enum.take(5) # => 1, 2, 4, 8, 16
内包表記
code:elixir
for x <- 1..5, do: 2*x # => 2, 4, 6, 8, 10
for x <- 1,2,3,4,5, y <- 1,2,3,4,5, x > y, do: x*y # => 2, 3, 6, 4, 8, 12, 5, 10, 15, 20
モジュール
code:elixir
defmodule SomeModule do
# パブリック関数
# @example SomeModule.greet "taro"
def greet(name) do
message = make_message(name)
IO.puts(message)
end
# プライベート関数
defp make_message(name) do
"Hello, #{name}"
end
end
シジル(Sigil)
正規表現(~r{...})等の~から始まる文法のこと
文字列と文字のリスト
Elixirでは"..."で囲まれたものを文字列、'...'で囲まれたものを文字のリストとして扱います
文字のリスト('...')は実体としては整数のリスト
code:elixir
is_list 'hello' # => true
is_list "hello" # => false
バイナリ
code:elixir
binary = << 1, 2, 3, 4 >>
byte_size binary # => 4
bit_size binary # => 32
# ビット数の指定
bit_size << 1::size(2) >> # => 2
byte_size << 1::size(2) >> # => 1
bit_size << 1::size(8), 2::size(4) >> # => 12
byte_size << 1::size(8), 2::size(4) >> # => 2
プロセス
Elixirにおけるプロセスについて
参考
プログラミングElixir
https://elixir-lang.org/getting-started/introduction.html