プログラミングElixir第3章、第4章

第3章 不変性

  • すべての変数は 不変 (Immutable) である。
  • 関数渡しもすべて値渡し(呼び出し元の変数は変化しない)である。

第4章 Elixirの基礎

組み込みの型

Elixir Schoolの基本 を参照したほうが詳しい.

整数

10進数、16進数(0x...)、8進数(0o...)、2進数(0b...)が使える。

iex(1)> 10
10
iex(2)> 0x10
16
iex(3)> 0o10  
8
iex(4)> 0b10
2
iex(5)> 1_0
10
iex(6)> 100_000_000
100000000
iex(7)> 0x100_0000
16777216
iex(8)> 0x1000000
16777216

浮動小数点

IEEE 754の倍精度ある。少数表示、Cほど自由度の高い指数表記ではなさそう。

iex(2)> 0.2   
0.2
iex(3)> 2e-1
** (SyntaxError) iex:3: syntax error before: e

iex(3)> 2.0e-1
0.2
iex(4)> 2.e-1
** (CompileError) iex:4: invalid call 2.e()
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
iex(4)> 2e-1
** (SyntaxError) iex:4: syntax error before: e

iex(4)> e-1
** (CompileError) iex:4: undefined function e/0
    (stdlib) lists.erl:1354: :lists.mapfoldl/3
iex(4)> 0.1e0
0.1
iex(5)> 0.1*1e0
** (SyntaxError) iex:5: syntax error before: e0

アトム

コロン : から始まる単一の語(アルファベット、数字、@ 記号)で、語の最後には !? の1文字付与は許される。

" で囲むと、文字列エスケープ?となるので、文字の制約はない。

純粋に文字列をタグとして使用するようで、Cでいうマクロ定数のような扱いか.

(定義不要なので スペルミスが致命的ですね)

範囲

start..end (where start, end は 整数)

正規表現

PCRE準拠の表現、Per5の正規表現相当。

~r{regexp} ~r{regexp}opts という表記ができる。{ の代わりに 任意の非アルファベット文字が使える。

iex(16)> Regex.run ~r{^(..*)/(..*)$}, "/path/to/where" 
["/path/to/where", "/path/to", "where"]
iex(17)> Regex.run ~r{^(..*)/(..*)$}r, "/path/to/where"
warning: the /r modifier in regular expressions is deprecated, please use /U instead
  (elixir) lib/regex.ex:848: Regex.translate_options/2
  (elixir) lib/regex.ex:179: Regex.compile/3
  (elixir) lib/regex.ex:207: Regex.compile!/2
  (elixir) lib/kernel.ex:4998: Kernel."MACRO-sigil_r"/3
  (elixir) src/elixir_dispatch.erl:186: :elixir_dispatch.expand_macro_fun/6

["/path/to/where", "/path", "to/where"]
iex(18)> Regex.run ~r{^(..*)/(..*)$}U, "/path/to/where"
["/path/to/where", "/path", "to/where"]

オプションに変更があった様子。

システム型

PID、ポート

プロセスIDのようなもの? ポートは...? また後で詳しく出てくるだろう。

iex(1)> self
#PID<0.105.0>

コレクション型

タプル

順序を持ったコレクション。python や Rustと同じかな。

{ m1, m2, n1, n2} { :ok, "value" } { :error, :enoent }

要素数は2~4で使うことが通例の様子。要素が増えるようなら、構造体やマップを使うとよいらしい。

よく使われるのが、関数呼び出しの結果を受け取るタプルを使う。 { 結果, 追加情報 } のスタイルで、結果は 正常終了時に :ok 異常時に :error というアトムを使うことが多いぽい。2つ目以降は 何らかの情報を返すことになるでしょう。

Cでいうポインタを渡して結果をもらってくる、ということができなく、関数の返値として受け取らなければならない。更新しあい場合は上書きする感じで。

リスト

[ 1, 2, 3 ] のような記述はすでに見てきた感じ。配列ではなく、 Lisp のリストに近いっぽい.

リストの演算も定義されている。

  • ++ は連結で、そのまま要素が連結される。
  • -- は差分で、右辺の要素を順番に検索して該当するものを削除していくようである。リスト最後まで該当しなければ何もせずに終わる。
iex(3)> [ 1, 2, 3, 4] ++ [5, 6] -- [6, 4]
[1, 2, 3, 4, 5]
iex(4)> [ 1, 2, 3, 4] ++ [5, 6] -- [3, 4]
[1, 2, 3, 4, 5, 6]
iex(5)> [ 1, 2, 3, 4] ++ [5, 6] -- [3, 2]
[1, 2, 3, 4, 5, 6]
iex(6)> [ 1, 2, 3, 4] ++ [5, 6] -- [6, 2]
[1, 2, 3, 4, 5]

リストに要素があるか、を知る in:

iex(1)> 1 in [2,3,4]
false
iex(2)> 3 in [2,3,4] 
true

キーワードリスト

キーと値のタプルのリスト。コマンドラインパラメータや関数のオプションの受け渡しに使うらしい。

この記述することが多々あるとのことで、シンタックスシュガーがある。

iex(1)> [ name: "Mike", address: "Osaka" ]
[name: "Mike", address: "Osaka"]
iex(2)> [ {:name, "Mike"}, {:address, "Osaka"} ]
[name: "Mike", address: "Osaka"]

iexでみると, タプルでの表示ではなく、キーワードリストそのもの? が出てくる。

要素が3つのタプルであれば、キーワードリスト表記にはならない様子。内部での管理はよくわからないけれど、通例では [ アトム: 値 ] と記述する。

iex(3)> [ {:name , "Mike" } ]
[name: "Mike"]
iex(4)> [ {:name , "Mike", "hoge" } ]
[{:name, "Mike", "hoge"}]

同じキーを並べることもできる...

iex(6)> [ name: "hoge", name: "huga", name: "anyone" ]
[name: "hoge", name: "huga", name: "anyone"]

マップ

キーと値のペア、のコレクション。連想配列のようなもの?

%{ key => value, key2 => value2 }

キーにアトムを使うと キーワードリストのような記述もできるようになる.

iex(1)> colors=%{ :red => 0xff0000, :green => 0x00ff00, :blue => 0x0000ff }
%{blue: 255, green: 65280, red: 16711680}
iex(2)> colors=%{ red: 0xff0000, green: 0x00ff00, blue: 0x0000ff }         
%{blue: 255, green: 65280, red: 16711680}
iex(3)> colors.red                                                         
16711680
iex(4)> colors[:red]
16711680

アトムキーワードを参照するときに . (dot)を使えるが、キーワードに存在しなければエラーになる。 [] で参照しようとすると nil が返ってくるので 処理しやすそう..

iex(5)> colors.yellow
** (KeyError) key :yellow not found in: %{blue: 255, green: 65280, red: 16711680}
iex(5)> colors[:yellow] 
nil

TODO: キーの一覧の取得方法、キーの存在確認 (nilを値として使う場合に困る?)

バイナリ

バイナリデータを扱うことができる。UTF文字列の表現に使われている。

<< 1,2,3 >>

ビット幅はデフォルトで 8bit のようであり、明示的に ::size(n) を付与することで ビット幅を指定する。

<< 1 :: size(4), 3 :: size(2), 1 :: size(2) >>

~演算子など

コーディングスタイルガイド が紹介されている。

  • キャメルケース(CamelCase):モジュール・レコード・プロトコル・ビヘイビア
  • その他:スネークケース(snake_case)

真偽値

true , false , nil が真偽値。それぞれアトムへのエイリアスらしい。厳格な比較で一致することから確認ができる(?)。

iex(1)> true === true
true
iex(2)> true === :true
true
iex(3)> false === :false
true

Cと同じように、 falsenil 以外はおおむね true として扱われ、本物の true に対して truthy と呼ぶ。

比較演算子

概説
a === b 厳格な同値性
a !== b 厳格な非同値性
a == b 同値性
a != b 非同値性
a > b 一般的な比較
a >= b ^
a < b ^
a <= b ^

整数と浮動小数の比較は 厳格に比較すると整数値であっても不一致となる。

iex(1)> 2==2.0
true
iex(2)> 2===2.0
false

ブール演算

Cの条件文のとおりに書くと、意図しない動きをする可能性もある(むしろ期待どおりか?)。

厳格な比較(この場合、aが trueかfalseを返すことを期待している)。

a or b
a and b
not a

ゆるい演算(aがtruthy...false/nil 以外であれば trueと扱われる)

a || b
a && b
!a

算術演算

+ - * / div rem

/ の結果は浮動小数となる。整数除算がしたければ div( Numerator, Denominator ) を使う。剰余は rem( Numerator, Denominator )

iex(1)> 3/2
1.5
iex(2)> div(3,2)
1
iex(3)> rem(3,2)
1
iex(4)> div(7,2)
3
iex(5)> rem(7,2)
1

with式

TODO: また使う頃に戻ってくる