第1回 - 基礎だ 1 -
GMO Pepabo, Inc.
Joe Honzawa
2015/5/13 Elixir勉強会
Elixirだ
今回の内容
> 概要
> 型と制御構文
> モジュールと関数
> パターンマッチとガード式
概要
Elixirとは
> Erlang VM上で動く関数型言語
> 並列分散
> 高可用性
> 動的型付け
> モダンな文法
> マクロによるメタプログラミング
> プロトコルによる多態性
Elixirをつくった人
> José Valim
> 歩く生産性
> rackやrailsにもコミット
Install
> $ brew install elixir
型と制御構文
型など
型など
> integer
> float
> atom
> boolean
> binary
> string
> list
> tuple
> map
> range
> function
> keyword list
など
IEx
> ポチポチしながら聞いてね
> $ iex
Integer
> 10
> 0b101
> 0o12
> 0x0a
Float
> 1.0
> 3.14
> 6.02e23
> 1.602e-19
> 1e3 ← これダメ
Atom
> Rubyで言うSymbol
> :atom
> :hello
> :’white spaces !?’
> nil == :nil
Boolean
> true
> false
> 実はatom
> :true == true
> :false == false
Binary
> <<1>>
> <<256>> == <<0>>
> <<1>> == <<1::size(8)>>
> <<1, 0>> == <<256::size(16)>>
> <<1::float>> ==
<<63,240,0,0,0,0,0,0>>
String
> “hello world”
> “世界を征服だ”
> 必ずダブルクォートしてー
> “hey” == <<104,101,121>>
> ‘hey’ == [104,101,121]
List
> 単方向リスト
> [1, 2, 3]
> [true, [1, 2, 3], “hey”]
> [1 | [2, 3]] == [1, 2, 3]
> [1, 2 | []] == [1, 2]
> 先頭への要素追加は高速
Tuple
> 値の組
> {:ok, 30}
> {1, 2, 3, 4, 5}
> ランダムアクセスはO(1)
> 更新や追加はコスト高
Map
> %{}
> %{“price”=> 3980}
> map = %{price: 100, amount: 2}
> map.price
> Map.get(map, :price)
> map = Map.put(map, :amount, 1)
Range
> 範囲
> 1..3
> 4..-5
> 1.2 .. 3.8
> (1.5 in 1.2 .. 3.8) == true
Function
> 無名関数
> fn (x, y) -> x * y end
> fun = &(&1 * &2) # キャプチャ記法
> fun.(3, 4) == 12 # call
> 定義済み関数のキャプチャ
> IOモジュールのputs関数(arity: 1)
> Enum.each([1, 2], &IO.puts/1)
Keyword List
> {key, val}なタプルのリスト
> kw = [{:age, 3}, … ]
> mapっぽく書いてよい
> kw == [age: 3, … ]
> Keyword.get(kw, :age) == 3
Keyword List
> リストなので
> 順序が保存される
> ランダムアクセスは遅め
> キーが同じなら先頭側が優先される
Keyword.get([a: 1, a: 2], :a) == 1
Keyword List
> configとして使われることが多い
> 引数として渡すとき
> 最後の引数ならば [] 省略可
Keyword.keys([a: 1, b: 2])
Keyword.keys(a: 1, b: 2)
紹介した型など
> integer
> float
> atom
> boolean
> binary
> string
> list
> tuple
> map
> range
> function
> keyword list
制御構文
case
case the_number do
1 -> “one”
2 -> “two”
3 -> “three”
end
if
if now == :morning do
“good morning”
else
“hi”
end
cond
cond do
rem(y, 400) == 0 -> “Yes”
rem(y, 100) == 0 -> “No”
rem(y, 4) == 0 -> “Yes”
true -> “No”
end
無いもの
> return
> 最後の評価値が返る
> loop, while
> 再帰で
モジュールと関数
関数
def
defmodule MyModule do
def print(arg) do
iikanjini_hyoji(arg)
end
defp iikanjini_hyoji(arg) do
IO.inspect arg
end
end
MyModule.print %{price: 298}
原則
> 全ての関数はモジュールに属する
> 名前か引数の数が違うなら別の関数
> add/2とsub/2は区別される
> sum/1とsum/2は区別される
慣習
> ワーカ関数の名前はdo_xxx
> モジュール名とファイル構造は対応させる
> lib/my_module/math.ex
> MyModule.Math
> 最も関心のあるものを第一引数に
> 例外を投げる関数の名前はxxx!
> 真偽値を返す関数の名前はxxx?
デフォルト引数
defmodule MyModule do
def say(word  “hi”) do
IO.puts word
end
end
MyModule.say(“hello”)
MyModule.say
ディレクティブ
directives
defmodule MyModule do
import Enum
alias Enum, as: E
require Integer
use ExUnit
end
import
defmodule MyModule do
import Enum, only: [count: 1]
def kazoeru(list) do
count(list) # Enum.count/1
end
end
MyModule.kazoeru [1, 2, 3]
MyModule.count [1, 2] → ❌
alias
defmodule MyModule do
alias Enum, as: E
def kazoeru(list) do
E.count(list) # Enum.count/1
end
end
MyModule.kazoeru [1, 2, 3]
as無しalias
defmodule MyModule do
alias Phoenix.HTML.Form
alias Phoenix.HTML.Form, as: Form
# 等価
end
requireとuse
マクロが関係するのでまた後で…
構造体
定義
defmodule User do
defstruct name: “”, age: 0
end
> 初期値が全てnilなら
defmodule User do
defstruct [:name, :age]
end
> 1モジュール1構造体
使用
user = %User{}
user = %User{name: "jack"} # 作成
user.name
Map.get(user, :name) # 値の取得
# パターンマッチと更新
def hbd(user = %User{age: age}) do
%User{user | age: age+1}
end
ここまでのまとめ
> defで関数を定義
> defmoduleでモジュールを定義
> 関数はモジュールの中で定義
> importで関数を取り込む
> aliasで別名をつける
> defstructで構造体を定義
パターンマッチと
ガード式
パターンマッチ
賢く束縛
> [x, y] = [1, 2]
> x == 1
> y == 2
> {1, x, 3} = {1, 2, 3}
> x == 2
アンスコ
> 使わない変数の頭につける
> 何にでもマッチ
> 値は要らないとき
> [head | _] = [1, 2, 3]
> {y, _m, _d} = {2015, 4, 30}
代表的な
> {:ok, res} = File.read “a.txt”
> [head | tail] = [1, 2, 3, 4]
> head == 1
> tail == [2, 3, 4]
スマートな
defmodule MyModule do
def same?(x, x), do: true
def same?(_x, _y), do: false
end
実際的な
case File.read(“a.txt”) do
{:ok, res} -> res
{:error, :enoent} ->
“oh it isn’t here”
{:error, :eacces} ->
“you can’t read it”
_ -> “?”
end
ピン止め
> x = 1
> [x, y, 3] = [2, 2, 3]
> x == 2, y == 2
> [^x, y, 3] = [2, 2, 3]
> MatchError
↑
再束縛されちゃってる
ガード式
defで
defmodule MyModule do
def sum(list) when is_list(list) do
# 略
end
def sum(_arg) do
IO.puts "the arg must be a list"
end
end
caseで
case the_number do
x when x in [1, 2, 3] ->
IO.puts "small"
_ ->
IO.puts "big or negative"
end
ガードで使える奴ら
> ==
> !=
> ===
> !==
> >
> <
> <=
> >=
> and
> or
> not
> !
> +
> -
> *
> /
> <> (左辺がリテラル)
> ++ (左辺がリテラル)
> in
> 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
> abs/1
> bit_size/1
> byte_size/1
> div/2
> elem2
> hd/1
> length/1
> map_size/1
> node/0
> node/1
> rem/2
> round/1
> self/0
> tl/1
> trunc/1
> tuple_size/1
ここまでのまとめ
> パターンマッチで賢く束縛
> 要らない変数はアンスコで始める
> ピンで再束縛を防ぐ
> パターンマッチとガードを合わせて
柔軟なマッチを
> ガードで使える関数は限られている
今回の内容
> Elixirとは
> 型と制御構文
> モジュールと関数
> パターンマッチとガード式

Elixirだ 第1回強化版 前半