本を先に進んでみよう。
2章はパターンマッチ。未だに慣れない。とりあえず=
でマッチ出来る。
左側に変数があると、マッチすると束縛が起きる。
左側の変数の値をマッチに使いたい場合は^
を付ける。
iex> a = 1 1 iex> [^a, b] = [2, 3] ** (MatchError) no match of right hand side value: [2, 3] iex> [a, b] = [2, 3] [2, 3] iex> a 2
ふむふむ。それ以外は、割と自然だった。
3章は不変性。まあ、関数型だからネ。
4章はElixirの基礎。まずは、組み込みの型。
整数は整数。浮動小数点数はIEEE754 倍精度。アトムはRubyでいうところのシンボルかな。シンタックスも同じだし。範囲もRubyのRange。
正規表現は~r{...}opts
。Rubyの%リテラルと同じように{ }は別の記号でもよい。オプションは見慣れないのがあるな・・・。
iex> Regex.replace ~r/,/, "a, b, c", "|" "a| b| c"
~r/..../
のように/を使うのはノスタルジーだと書かれている(笑)。
システム型として、PIDとポート、リファレンスがあるらしいが後回しにされてる。
コレクションとしては、タプルとリスト、マップとバイナリ。
タプルは、まあ、タプル。RubyならArray。何を入れてもいい。
リストは、いわゆるconsリスト。後で詳しく出てくると思う。あと、キーワードリストという特別扱いされる形式がある。
iex(17)> [a, b] = [name: "Taro", age: 5] [name: "Taro", age: 5] iex(18)> a {:name, "Taro"}
Rubyのハッシュの記法みたいだけど、実体はタプルのリスト。関数呼び出しの最後の引数がキーワードリストの場合、角括弧を省略できる。 つまり、キーワード引数はこう実装されていると。
マップはRubyでいうところのハッシュ。記法も似てる。キーがアトムのときに%{ :key => value}
を%{key: value}
と書いていいのも似てる。
取り出す時は、map1[:key]
と書いても良いし、map1.key
でもいい。
こっちをキーワード引数に使わなかったのは、キーワードリストはキーの重複が許されるかららしい。
バイナリはビットストリームを扱う。面白い。
日付型。こんな感じ。リテラル表現の仕方が面白い。シジルっていうらしい。
iex> {:ok, d1} = Date.new(2020, 12, 28) {:ok, ~D[2020-12-28]} iex> d1 ~D[2020-12-28] iex> d2 = ~D[2020-12-28] ~D[2020-12-28] iex> d1 == d2 true iex> Date.day_of_week(d1) 1 iex> Date.add(d1, 4) ~D[2021-01-01] iex> inspect d1, structs: false "%{__struct__: Date, calendar: Calendar.ISO, day: 28, month: 12, year: 2020}"
時刻型。1秒以下の値をどう持つかがちょっと難しい。「マイクロ秒と有効桁のタプルを持つ」と説明されているけど、ようわからん。
iex> {:ok, t1} = Time.new(12, 23, 34) {:ok, ~T[12:23:34]} iex> {:ok, t2} = Time.new(12, 23, 34.56) ** (FunctionClauseError) no function clause matching in Time.new/5 The following arguments were given to Time.new/5: # 1 12 # 2 23 # 3 34.56 # 4 {0, 0} # 5 Calendar.ISO Attempted function clauses (showing 2 out of 2): def new(hour, minute, second, microsecond, calendar) when is_integer(microsecond) def new(hour, minute, second, {microsecond, precision}, calendar) when is_integer(hour) and is_integer(minute) and is_integer(second) and is_integer(microsecond) and is_integer(precision) (elixir 1.11.2) lib/calendar/time.ex:119: Time.new/5
このように秒に小数は与えられない。
これはOKみたい。
iex> {:ok, t2} = Time.new(12, 23, 34, 56) {:ok, ~T[12:23:34.000056]}
リテラルとしては、小数の秒を持てる。が、扱いが難しい。
iex> t1 = ~T[12:23:34.0] ~T[12:23:34.0] iex> t2 = ~T[12:23:34.00] ~T[12:23:34.00] iex> t1 == t2 false
これが同じにならないのは、有効桁が違うから、らしい。
DateTimeはタイムゾーンを持つ。タイムゾーンを持たないNaiveDateTimeもある。
真偽値は、true, false, nilの3種類。Rubyと同じかな。falseとnil以外の値が真と扱われることが多いのもRubyと同じ。 ちなみにtrueは:trueとおなじもの。
比較演算子の===
はRubyと違って厳密な同値を取る。つまり、1 == 1.0
はtrue, 1 === 1.0
はfalse。
ちなみに、Rubyの===は・・・なんて呼ぶんだろう。むしろ、より緩い比較になる。case式で使う目的で、いろんなクラスで良い感じに定義してある。
irb> /hoge/ === "hogefuga" => true irb> (2..4) === 3 => true
ブール演算子はandとorのペアと、&&と||のペアがある。andとorは左側(= 1つ目の引数)にtrueかfalseが来ることを期待する。&&はfalseとnil以外はtrueと解釈する。つまり、Perlのor die
をしたければ||
を使う・・・ということなのか?
コレクションにアイテムが含まれるかどうかはin演算子で確かめる。マップはキーと値のタプルが必要。
iex> 1 in [1, 2, 3] true iex> {key1: "value1"} in %{key1: "value1", key2: "value2"} ** (SyntaxError) iex:38:2: syntax error before: key1 iex> {:key1, "value1"} in %{key1: "value1", key2: "value2"} true iex> {:key2, "value1"} in %{key1: "value1", key2: "value2"} false
次に、変数のスコープ・・・といっても特に特殊なことはなくて、普通のレキシカルスコープ。
変わった例として、with式がある。Pythonのwith構文みたいにブロックの中から外に漏れて欲しくないリソースを扱える。 withでパターンマッチして、取り出したものは、doブロックの中だけで使える。
iex> a = [1, 2, 3] [1, 2, 3] iex> x = with [_, 2, i] = a do ...(42)> i * 3 ...(42)> end 9
パターンマッチに失敗すると=
では例外が出るが、<-
を使うとパターンマッチの右側がそのまま返る。
iex> x = with [_, 1, i] = a do ...(44)> i * 3 ...(44)> end ** (MatchError) no match of right hand side value: [1, 2, 3] iex> x = with [_, 1, i] <- a do ...(44)> i * 3 ...(44)> end [1, 2, 3]
doのショートカットというのもある。Pythonっぽい?
iex> x = with [_, 1, i] <- a, ...(45)> do: i * 3 [1, 2, 3] iex> x = with [_, 2, i] <- a do: i * 3 ** (CompileError) iex:46: undefined function a/1 (stdlib 3.14) lists.erl:1358: :lists.mapfoldl/3 iex> x = with [_, 2, i] <- a, do: i * 3 9
ちょっと混乱するね