技術のメモ帳

気が向いたときに書いてます

elixir学習日誌: データ型〜パターンマッチング

学習動機

不純な動機は、ひとつもありません。

ゴール

  • Phoenixを使って、APIサーバを立てる。

余談

たいていどんな言語も、次のパスをなぞれば、なんかしらのカタチにはなる...と思っている。

  1. FizzBuzzしながらシンタックスを覚える。
  2. 主要ライブラリを“使う”
  3. フレームワークに触れる
  4. APIをサーブする
  5. ファイル操作関連を覚える
  6. DBと連携する
  7. フレームワークで何か作る

教材

ラブライブは知らないので、どんな世界観が分からなかったが、非常に丁寧に解説されていた...有り難い)

インストール

データ型

Type Content
integer 整数
float 浮動小数
atom アトム
string 文字列
list リスト
tuple タプル
map マップ
keyword キーワードリスト

整数・浮動小数

1      # integer (decimal)
0x1F   # integer (hexdecimal)
0o765  # integer (octal)
0b1010 # integer (binary)
1.0    # float
1 + 2  # => 3
3 - 1  # => 2
5 * 5  # => 25
10 / 2 # => 5.0 必ずfloat
rem 10, 3   # => 1
max 3, 2    # => 3
min 3, 2    # => 2
round 2.52  # => 3
trunc 2.52  # => 2

文字列

"ハロー" # => "ハロー"              : string
'ハロー' # => [12495, 12525, 12540] : list
  • 結合は<>を使う
"foo" <> "bar"   # => foobar
"#{20 + 5} year" # => 25 year
String.length "hello"       # => 5
byte_size "hello"           # => 5
String.at "hello", 1        # => "e"
String.split "foo,bar", "," # => ["foo", "bar"]

リスト

  • 飛び飛びのメモリ領域を結びつける。
  • 先頭から順に辿っていく(検索時にオーバーヘッドがある)
  • ランダムアクセスには向かない。
[1, :foo, "bar"]            # => [1, :foo, "bar"]
hd [:foo, :bar, "baz"]      # => :foo
tl [:foo, :bar, "baz"]      # => [:bar, "baz"]
Enum.at [25, :num], 1       # => :num
Enum.at [25, :num], 2       # => nil
length [1, 2, 3]            # => 3
Enum.sum [1, 2, 3]          # => 6
Enum.reverse [1, 2, 3]      # => [3, 2, 1]
List.first [1, 2, 3]        # => 1
List.last [1, 2, 3]         # => 3
List.flatten [[1, 2], 3, 4] # => [1, 2, 3, 4]

タプル

  • 連続したメモリ領域を確保するので、アクセスしやすい(同じ処理時間でたどれる)
  • ランダムアクセスに向いている。
{1, 2, 3}
elem {1, 2, 3}, 1 # => 2
  • タプルは変更には向かない。できないこともないけれど。
Tuple.delete_at {1, 2, 3}, 0 # => {2, 3}
put_elem {"a", "b"}, 1, "c"  # => {"a", "c"}
tuple_size {1, 2, 3}         # => 3
  • 長さを計算する関数はlength、決まっている長さを返すのはsizeって名前を使う
  • タプルは最初から長さが決まっているので「size」を使う
  • リストとタプルは相互変換可能
List.to_tuple [:foo, :bar, :baz] # => {:foo, :bar, :baz}
Tuple.to_list {1, 2, 3}          # => [1, 2, 3]

マップ

  • リストやタプルに近いデータの集まり。
  • 順序はないが、データに見出しをつけられる。
%{:umi => "foo", :sora => "bar"}
%{:umi => "foo", :umi  => "bar"} # => %{umi:"bar"}
  • キーの重複はできない仕様で、後から書いた方が優先される。
  • 添字で値を取得できる。
%{:foo => 1, :bar => 2, :baz => 3}[:baz] # => 3
  • 値は更新しながらもデータは不変なのはElixirの特徴
%{foo: 1, bar: 2, baz: 3}
  • ファットアローを使わない書き方もできる。
Dict.size %{ foo: 1, bar: 2, baz: 3 }                # => 3
Dict.keys %{ foo: 1, bar: 2, baz: 3 }                # => [:foo, :bar, :baz] ※順序不確定
Dict.split %{ foo: 1, bar: 2, baz: 3 }, [:foo, :bar] # => [%{ bar: 2, foo: 1 }, %{ baz: 3 }]
  • splitは指定した引数を含むもの/含まないものを分割する

キーワードリスト

[{:foo, 1}, {:bar, 2}, {:baz, 3}] # => [foo: 1, bar: 2, bar: 3]
  • 左辺の省略記法がキーワードリスト
  • 最初の要素がアトムになっているタプルのリスト
[foo: 1, bar: 2, baz: 3] ++ [foobar: 4] # => [foo: 1, bar: 2, baz: 3, foobar: 4]
  • リストなので、リストと同じ操作ができる
length [foo: 1, bar: 2, baz: 3] # => 3
  • マップのような要素アクセスも可能
[foo: 1, bar: 2, baz: 3][:foo] # => 1
  • ただし、あくまでリストなので、ランダムアクセスには向いていない。

真偽値

  • == と === は厳密に型まで比較している(JavaScript風味?)

パターンマッチング

  • = 演算子のニュアンスを知る。
  • 左右の値が同じかどうか比較する。
  • 変数の場合は代入が行われる。
foo   = "bar"
"bar" = foo  # => "bar"
"baz" = foo  # => MatchError
  • = は「比較と代入ができる便利な演算子」と覚えるとよい。
{foo, bar, baz} = {1, 2, 3}
foo # => 1
  • タプルでマッチさせると、多重代入にも使える。
{foo, bar, baz} = [1, 2, 3]    # => MatchError
{foo, bar, baz} = {1, 2, 3, 4} # => MatchError
  • 両辺の型が異なると例外が発生する。
  • 両辺の要素数が合っていないと例外が発生する。
[head | tail] = [:foo, :bar, :baz]
IO.puts head # => :foo
IO.puts tail # => [:bar, :baz]
  • | を使うと、先頭とそれ以外で分割できる。
{name, name} = {"taro", "taro"}   # => {"taro", "taro"}
{name, name} = {"taro", "hanako"} # => MatchError
  • 同じ変数を2回利用できるが、異なる値は代入できない。
  • Elixurでは基本的に再代入を行わない
{^name, name} = {"taro", "hanako"}
  • ^ はピン演算子で、代入の対象とはならない。
{_, _, foo} = {1, 2, 3}
IO.puts _ # => CompileError
  • _ は何にでもマッチするので、パターンの一部を無視したいときに使う
  • 変数ではないので後から参照することはできない。

辛み