プログラミングElixir第5章

第5章 無名関数

記述方法

fn parameter-list -> body end

関数とパターンマッチ

関数を定義してみます.

iex(1)> fn (a,b) -> a+b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(2)> add = fn (a,b) -> a+b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> add (1,2)
** (SyntaxError) iex:3: unexpected parentheses. If you are making a function call, do not insert spaces between the function name and the opening parentheses. Syntax error before: '('

iex(3)> add. (1,2)
3

練習問題

iex(2)> list_concat= fn a,b -> a ++ b end
#Function<12.128620087/2 in :erl_eval.expr/5>
iex(3)> list_concat.( [:a, :b], [:c, :d])
[:a, :b, :c, :d]
iex(4)> list_concat.( 2, 4)
** (ArgumentError) argument error
    :erlang.++(2, 4)

TODO: 関数の引数の型をマッチングさせる方法... → "ガード節" を使う。 when キーワードを

iex(1)> pair_tuple_to_list = fn {a,b} -> [a, b] end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> pair_tuple_to_list.( { 1234, 5678 } )
[1234, 5678]

関数中に複数のbody

引数がタプルの場合? マッチングにより実行する処理を変更できる。 C言語のif文とは違って、Elixirはパターンマッチにより処理を分岐する(雰囲気)。

fn
  {:ok, file} -> "Open success",
  {_, error}  -> "Error: #{:file.format_error(error)}"
end

練習問題

iex(1)> fb = fn
...(1)>  0, 0, _ -> "FizzBuzz"
...(1)>  0, _, _ -> "Fizz"
...(1)>  _, 0, _ -> "Buzz"
...(1)>  _, _, x -> x
...(1)> end
#Function<18.128620087/3 in :erl_eval.expr/5>
iex(2)> fb.(0,0,0)
"FizzBuzz"
iex(3)> fb.(0,0,1)
"FizzBuzz"
iex(4)> fb.(0,1,0)
"Fizz"
iex(5)> fb.(0,1,1)
"Fizz"
iex(6)> fb.(1,0,0)
"Buzz"
iex(7)> fb.(1,0,1)
"Buzz"
iex(8)> fb.(1,1,0)
0
iex(9)> fb.(1,1,1)
1


iex(14)> a=fn a -> fb.(rem(a,3),rem(a,5),a) end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(15)> a.(10)
"Buzz"
iex(16)> a.(11)
11
iex(17)> a.(12)
"Fizz"
iex(18)> a.(13)
13
iex(19)> a.(14)
14
iex(20)> a.(15)
"FizzBuzz"
iex(21)> a.(16)
16
iex(22)> a.(17)
17

関数を返す関数

fn...end は 関数を作って返す。変数に束縛(どっちがどっち?)されて、使用するときは ".()" で関数呼び出しになる...デリファレンス?

iex(1)> add_n = fn n -> (fn other -> n + other end) end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> add_three = add_n.(3)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(3)> add_six = add_n.(6)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(4)> add_three.(8)
11
iex(5)> add_six.(2)
8

上記の例のように、似たような関数を作る場合に便利が良い。外側の関数引数を、内側(返すほう)の body で参照すると、その値を使った関数が返される。

Cで関数をマクロで生成していたようなことをスマートにできるという感じ..か.

&記法

fn..end を省略すると &() とかける。タプルを返す関数は &{} 関数引数は &amp;n で記述し、n番目の引数は n=1,2,.. となる。

iex(1)> add_one = &(&1+1)
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(2)> add_one.(2)
3

iex(3)> add_one = fn n -> n+1 end
#Function<6.128620087/1 in :erl_eval.expr/5>
iex(4)> add_one.(2)
3

定義済み関数の名前なし関数が取得できる。

iex(4)> &length/1
&:erlang.length/1
iex(5)> &length/2
** (UndefinedFunctionError) function :erl_eval.length/2 is undefined or private
iex(4)> a=&length/1
iex(5)> a=&length/1
&:erlang.length/1
iex(6)> a.("hoge")
** (ArgumentError) argument error
    :erlang.length("hoge")
iex(6)> a.([1,2,2])
3

演習

iex(1)> Enum.map [1,2,3,4], fn x -> x + 2 end
[3, 4, 5, 6]

iex(2)> Enum.map [1,2,3,4], &(&1+2)
[3, 4, 5, 6]
iex(2)> Enum.each [1,2,3,4], fn x -> IO.inspect x end
1
2
3
4
:ok
iex(3)> Enum.each [1,2,3,4], &(IO.inspect &1)
1
2
3
4
:ok
iex(4)> Enum.each [1,2,3,4], &IO.inspect &1
1
2
3
4
:ok

プログラミング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: また使う頃に戻ってくる

プログラミングElixir第2章

第2章 パターンマッチ

Cでいうところの代入記述は、Elixirではパターンマッチになる。 = 演算子で左右のマッチングが取れれば、変数に対応する値を束縛(代入?参照?)する。

iex(1)> a=1
1
iex(2)> a
1
iex(3)> 1=a
1
iex(4)> a=2
2
iex(5)> 1=a
** (MatchError) no match of right hand side value: 2

リスト表記は [1,2,3] のように書くらしい。リストの要素がリストでもあって良い。二次元配列ではない(要素ごとに任意の型?をおける)。 [[1,2,3],[4,5]]

iex(6)> [[1,2,3],[4,5]]
[[1, 2, 3], [4, 5]]
iex(7)> [_,a]=[[1,2,3],[4,5]]
[[1, 2, 3], [4, 5]]
iex(8)> a
[4, 5]

ここで _ は、マッチングさせるために使うけれども、あとで参照しないような場合に用いる変数である。要は捨てたい場合に使うもの?(通常の変数に比べると高速になるのかもしれない)。

一つの式に現れる変数は、最初にマッチングした値とマッチする必要がある。

iex(1)> [a,a] = [1,1,1]
** (MatchError) no match of right hand side value: [1, 1, 1]
iex(1)> [a,a] = [1,1]  
[1, 1]
iex(2)> [a,a] = [1,2]
** (MatchError) no match of right hand side value: [1, 2]

最初のマッチではなく、すでに束縛した値をマッチングに使う場合は ^を変数の頭につける

iex(1)> a=1
1
iex(2)> ^a=1
1
iex(3)> ^a=2
** (MatchError) no match of right hand side value: 2

これはなぜだめなのかな...

iex(4)> 1=^a
** (CompileError) iex:4: cannot use ^a outside of match clauses

TODO: あとでしらべる

2020/06/16(火)Elixirを学ぶ

動機

さる SWEST-21 において、 Nervesハンズオンを体験した。

ここでElixir, earlangについて知るきっかけとなる。組み込み向けのシステムとして使用されており、サーバサイドのNerves Hubから末端のNerves device(raspberry Pi, Beagle Bone)までサポートされていた。

OTA(Over The Air)による自動デプロイがサポートされていたことがとても印象強く、安定した(比較的リアルタイム性を必要としない)システムを構築できそう、という感じを受けました。

なにより開発者の一人である, Justin氏が来日されていたのも大きいですね。

環境を探す

editor

IntelliJやCLionだとプラグインがある.

vim-plugin

語彙

初めて触れたとき、単語の意味がわからなかった。正確性にかけるかもしれないけれど、以下に記録しておく:

概要?
Earlang Erlangは数学者のアグナー・アーランから名前をとって命名された。Ericson社が1998年にOSS化。
OTP Elixirで Earlを使うための結合部分?
Elixir Earlを使っている新たな言語. Rubyの影響を大きく受けている?
mix ビルドシステム?
Hex パッケージ管理システム
asdf Earlang/Elixirをソースビルドする管理システム??(shell script)

公式?

入門記事

Phoenix (WEB framework)

書籍類

プログラミング Elixir(AA)