undefined

bokuweb.me

ElixirですごいE本 1章

すごいE本を読みつつElixirを学んでみる。

すごいErlangゆかいに学ぼう!

すごいErlangゆかいに学ぼう!

すでにヽ(´・肉・`)ノログ さんが実施されていた。

2014/07/28/すごいE本をElixirでやる - ヽ(´・肉・`)ノログ

15/10/15現在はElixirのバージョンは 1.1.1。

1章

ErlangシェルはElixirではiexに相当。 簡単に試せるところはiexで試していく。

1.2 Erlangの基礎をいくつか

数値型

  • 2進数は0b、8進数は0o、16進数は0xで始める。
iex(1)> 2+15
17
iex(2)> 49 * 100
4900
iex(3)> 1892-1472
420
iex(4)> 5/2
2.5
iex(5)> div(5,2)
2
iex(6)> rem(5,2)
1
iex(7)> (50*100)-4999
1
iex(8)> -(50*100-4999)
-1
iex(9)> -50*(100-4999)
244950
iex(10)> 0b101010
42
iex(11)> 0o677
447
iex(12)> 0xAE
174

変化できない変数

  • Erlangでは値を変数に与えることは1回しかできないがElixirでは再束縛が可能。
  • Erlang同様再束縛を禁止する場合は^を付ける。
iex(1)> one = 1
1
iex(2)> un = uno = one = 1
1
iex(3)> un
1
iex(4)> uno
1
iex(5)> two = one + one
2
iex(6)> two = 2
2
iex(7)> two = two + 1
3
  • =演算子の動作は代入ではなくmatch operator
  • 左辺が変数でパターンマッチした場合に右辺が束縛される。
  • マッチしないとMatchErrorが発生する。
  • 再束縛が可能、再束縛を禁止する場合は^すなわちpin operatorを使う。
  • 再束縛可能なのでf().に相当するものは存在しない?
iex(1)> two = 2
2
iex(2)> ^two = two + 1
** (MatchError) no match of right hand side value: 3

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

iex(2)> two = 3
3

アトム

アトムはリテラルで、自分自身の名前を値として保持しています。見たままの値が得られます。それ以上の何ものでもありません。 (すごいErlangゆかいに学ぼう! 頁6より)

  • Erlangは変数を大文字で、アトムを小文字で始める。Elixirではアトムは:で始める。
  • : で始まったダブルクォートで囲った値もアトム。
iex(1)> :atom
:atom
iex(2)> :atoms_rule
:atoms_rule
iex(3)> :atoms_rule@elixir
:atoms_rule@elixir
iex(4)> :"Atoms can be cheated!"
:"Atoms can be cheated!"
iex(5)> :atom = :'atom'
:atom
iex(6)> :atom = :"atom"
:atom
iex(1)> true == :true
true
iex(2)> is_atom(false)
true
iex(3)> is_boolean(:false)
true
iex(4)> is_boolean(false)
true

アトムはアトム表内で参照されていて、これはメモリを消費します(1アトムにつき、32ビットシステムでは4バイト、64ビットシステムでは8バイト、それぞれ消費します)。アトム表はガベージコレクトの対象にならないので、アトムはシステムが落ちるか1048577個のアトムが宣言されるまで蓄積されていきます。これはつまり、アトムは動的に生成すべきではないということを意味します。 (すごいErlangゆかいに学ぼう! 頁7より)

ブール代数と比較演算子

  • xorはない。and,or,notがある。
  • and,or,notはブール代数にのみ使える?その他には&&,||がある。
  iex(1)> 5 and 5
  ** (ArgumentError) argument error: 5

  iex(1)> 1 and true
  ** (ArgumentError) argument error: 1

  iex(1)> not 1
  ** (ArgumentError) argument error
    :erlang.not(1)
iex(1)> true and false
false
iex(2)> false or true
true
iex(3)> true xor false
** (SyntaxError) iex:3: syntax error before: 'xor'

iex(3)> true & false
** (SyntaxError) iex:3: syntax error before: '&'

iex(3)> true && false
false
iex(4)> not false
true
iex(5)> not ( true and true)
false
  • 厳格な比較は===,!==、そうでない場合は==,!=
iex(1)> 5 === 5
true
iex(2)> 1 === 0
false
iex(3)> 1 /== 0
** (SyntaxError) iex:3: syntax error before: '=='

iex(3)> 1 /= 0
** (SyntaxError) iex:3: syntax error before: '='

iex(3)> 1 !== 0
true
iex(4)> 1 != 0
true
iex(5)> 5 !== 5.0
true
iex(6)> 5 != 5.0
false

要素の比較順はErlangと同様?

number < atom < reference < fun < port < pid < tuple < list < bit string 「順序は重要ではないのです-すべての並び替え順序(全順序)が明確に定義されていることが重要なのです」 (すごいErlangゆかいに学ぼう! 頁9より)

iex(1)> 1 < 2
true
iex(2)> 1 < 1
false
iex(3)> 1 >= 1
true
iex(4)> 1 =< 1
** (SyntaxError) iex:4: syntax error before: '<'

iex(4)> 1 >= 1
true
iex(5)> 1 => 1
** (SyntaxError) iex:5: syntax error before: '=>'

iex(5)> 5 + 11ama
** (SyntaxError) iex:5: syntax error before: ama

iex(5)> 5 === true
false
iex(6)> 0 == false
false
iex(7)> 0 === false
false
iex(8)> 1 < false
true

タプル

  • _は気にしない変数、常に未束縛と認識されるワイルドカードのような振る舞い
  • アトムを1つめの要素に含むタプルはタグ付きタプルと呼ばれる
iex(1)> x = 10
10
iex(2)> y = 4
4
iex(3)> point = {x, y}
{10, 4}
iex(4)> point
{10, 4}
iex(5)> point = {4, 5}
{4, 5}
iex(6)> {x,y} = point
{4, 5}
iex(7)> x
4
iex(8)> {x,_} = point
{4, 5}
iex(9)> {_,_} = {4,5}
{4, 5}
iex(10)> {_,_} = {4,5,6}
** (MatchError) no match of right hand side value: {4, 5, 6}

iex(10)> temperature = 23.213
23.213
iex(11)> preciseTemperature = {:celsius, 23.213}
{:celsius, 23.213}
iex(12)> {:kelvin, t} = preciseTemperature
** (MatchError) no match of right hand side value: {:celsius, 23.213}

iex(12)> {:point, {x, y}}
{:point, {4, 5}}

リスト

  • リストの中の数字のうち、一つでも文字として表示できないものがあると数字のリストとして数字を表示する
iex(1)> [1,2,3, {:number, [4.5,6]}, 5.34, :atom]
[1, 2, 3, {:number, [4.5, 6]}, 5.34, :atom]
iex(2)> [97,98,99]
'abc'
iex(3)> [97,98,99,4,5,6]
[97, 98, 99, 4, 5, 6]
iex(4)> [233]
[233]
iex(5)> [120,121,122]
'xyz'
  • リストの結合は++,削除は--
  • ++,--は右から左に実行されていく
iex(6)> [1,2,3] ++ [4,5]
[1, 2, 3, 4, 5]
iex(7)> [1,2,3,4,5] -- [1,2,3]
[4, 5]
iex(8)> [1,2,3,4,5] -- [1,3]
[2, 4, 5]
iex(9)> [1,2,3,4,5] -- [1,4]
[2, 3, 5]
iex(10)> [2,4,2] -- [2,4]
[2]
iex(11)> [2,4,2] -- [2,4, 2]
[]
iex(12)> [1,2,3] -- [1,2] --[3]
[3]
iex(13)> [1,2,3] -- [1,2] --[2]
[2, 3]
iex(14)> hd([1,2,3,4])
1
iex(15)> tl([1,2,3,4])
[2, 3, 4]
iex(16)> list = [2,3,4]
[2, 3, 4]
iex(17)> newList = [1|list]
[1, 2, 3, 4]
iex(18)> [head|tail] = newList
[1, 2, 3, 4]
iex(19)> head
1
iex(20)> tail
[2, 3, 4]
iex(21)> [newHead | newTail] = tail
[2, 3, 4]
iex(22)> newHead
2
  • |はCons operatorと呼ばれる
iex(23)> [1|[]]
[1]
iex(24)> [2 | [1 | []]]
[2, 1]
iex(25)> [3|[2 | [1 | []]]]
[3, 2, 1]

リスト内包表記

  • Elixirではforが使える
iex(1)> for n <- [1, 2, 3, 4], do: 2 * n
[2, 4, 6, 8]
iex(2)> for x <- 1..10, rem(x,2) === 0, do: x
[2, 4, 6, 8, 10]
iex(3)> restaurantMenu = [{:steak, 5.99}, {:beer, 3.99}, {:poutine, 3.50}, {:kit
ten, 20.99}, {:water, 0.00}]
[steak: 5.99, beer: 3.99, poutine: 3.5, kitten: 20.99, water: 0.0]
iex(4)> for {item, price} <- restaurantMenu, price >= 3, price <= 10, do: {item,
 price * 1.07}
[steak: 6.409300000000001, beer: 4.2693, poutine: 3.745]
iex(5)> for x <- [1,2], y <- [3,4], do: x + y
[4, 5, 5, 6]
iex(6)> weather = [{:tronto, :rain}, {:montreal, :storms}, {:london, :fog}, {:pa
ris, :sun}, {:boston, :fog}, {:vancouver, :snow}]
[tronto: :rain, montreal: :storms, london: :fog, paris: :sun, boston: :fog, vanc
ouver: :snow]
[tronto: :rain, montreal: :storms, london: :fog, paris: :sun, boston: :fog,
 vancouver: :snow]

1.3 バイナリデータを扱う

ビット構文

2014/08/04/すごいE本をElixirでやる(6) - ヽ(´・肉・`)ノログ

Elixir のバイナリのパターンマッチは - (値) - (値)::(サイズ) - (値)::(型指定子リスト) - (値)::(サイズ)-(型指定子リスト)

iex(1)> color = 0xF09A29
15768105
iex(2)> pixel = <<color:24>>
** (SyntaxError) iex:2: keyword argument must be followed by space after: color


iex(2)> pixel = <<color::24>>
<<240, 154, 41>>
iex(3)> pixel = <<color::28>>
<<15, 9, 162, 9::size(4)>>
iex(4)> pixel = <<color::15>>
<<52, 41::size(7)>>
iex(5)> pixel = <<color::12>>
<<162, 9::size(4)>>
iex(6)> pixel = <<color::16>>
<<154, 41>>
iex(7)> pixel = <<color::32>>
<<0, 240, 154, 41>>
iex(8)> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(9)> <<pix1, pix2, pix3, pix4>> = pixels
** (MatchError) no match of right hand side value: <<213, 45, 132, 64, 76, 32,
6, 0, 0, 234, 32, 15>>

iex(9)> <<pix1::24, pix2::24, pix3::24, pix4::24>> = pixels
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(10)> <<r::8, g::8, b::8>> = <<pix1::24>>
<<213, 45, 132>>
iex(11)> r
213
iex(12)> <<r::8, rest::binary>> = pixels
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(13)> r
213
iex(14)> rest
<<45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(15)> <<x1::unsigned>> = <<-44>>
<<212>>
iex(16)> <<x2::signed>> = <<-44>>
<<212>>
iex(17)> x2
-44
iex(18)> <<x2::integer-signed-little>> = <<-44>>
<<212>>
iex(19)> x2
-44
iex(20)> <<n::8-unit(1)>> = <<72>>
"H"
iex(21)> <<n::integer>> = <<72>>
"H"
iex(22)> <<y::4-little-unit(8)>> = <<72, 0, 0, 0>>
<<72, 0, 0, 0>>
iex(23)> y
72

ビット単位のバイナリ操作

iex(1)> use Bitwise
nil
iex(2)> bnot 1
-2
iex(3)> 1 &&& 1
1
iex(4)> 0b00100 = 0b00010 >>> -1
4

バイナリ文字列

  • 使い所がわからない
iex(1)> <<"this is a binary string!">>
"this is a binary string!"

バイナリ内包表記

  • forが使える

2014/08/05/すごいE本をElixirでやる(7) - ヽ(´・肉・`)ノログ

for <>>>, rem(x, 2) === 0, do: x と >>>> を繋げて書いてしまうと,パーサーが上手く文を区切ってくれないので必ずスペースを開けなければならない. あと Elixir の内包表記は何も指定しないと常にリストが返るので,バイナリを返したいときには into を指定すること.

  • 返却値のデフォルトはリストだが、intoオプションを使うことで任意の構造で受け取ることができる
iex(1)> for <<x <- <<1,2,3,4,5>> >>, rem(x,2) === 0, do: x
[2, 4]
iex(2)> pixels = <<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
<<213, 45, 132, 64, 76, 32, 76, 0, 0, 234, 32, 15>>
iex(3)> rgb = for <<r::8, g::8, b::8 <- pixels >>, do: {r, g, b}
[{213, 45, 132}, {64, 76, 32}, {76, 0, 0}, {234, 32, 15}]
iex(5)> for <<c <- " hello world ">>, c != ?\s, into: "", do: <<c>>
"helloworld"
iex(9)> Enum.into(["a", "b", "c"], "")
"abc"