|> Weeeeeeb

どんどんドーナツどーんと行こう!(10万円山さん)

すごいErlangゆかいに学ぼうをelixirで書きなおしてみた(第一章)

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

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

Basic types - Elixir

目次(1章)

  • Erlangシェルを使ってみる
  • Erlangの基礎をいくつか
  • バイナリデータを扱う

すごいErlangゆかいに学ぼう!を
elixirで書きなおしてみる。

現時点での私のステータスとしては、
elixirの開発環境を整備したくらい。
いわゆる初心者。

Erlangに詳しい訳でもないので、
違いを記述していくというよりも、
書籍に記載されているシンタックスをelixirで書き直します。

また、これは下記の方々の完全にパクリになってしまってます。
本を買う前にググってとけって話ですね。
私よりも読みやすい記事となっていると思うので、
私の記事は余談的に閲覧していただければ。

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

blog.bokuweb.me

(仮想)環境

(作業)環境

開発環境の構築に関しては、
私が以前作成した記事を参照していただければ

kuriya0909.hatenablog.com

Erlangシェルを使ってみる

シェルの起動

Erlangシェルの起動はerl
-> elixirではiex

シェルの終了

どちらもCtrl+Cを2回

Erlangの基礎をいくつか

数値型

四則演算は下記のとおりですね。
気をつけたいのは、整数の同士の除算において
小数点以下まで扱っている所ですかね。

        Interactive Elixir (1.2.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
    iex(1)> 2 + 15
    17
    iex(2)> 49 * 100
    4900
    iex(3)> 1892 - 1472
    420
    iex(4)> 5 / 2
    2.5

整数桁のみで除算を行うには、
下記の関数を使用します。
div関数が商を、remが余りを算出します。

   iex(5)> div 5, 2
    2
    iex(6)> div(5, 2)
    2
    iex(7)> rem 5, 2
    1
    iex(8)> rem(5, 2)
    1

計算式の優先順位も通常通りです。

   iex(9)> (50 * 100) - 4999
    1
    iex(10)> -(50 * 100 - 4999)
    -1
    iex(11)> -50 * (100 - 4999)
    244950

各種進数の扱いについて、Erlangと違いがあります。
Erlang#の前に扱う進数を記述します。
e.g. 2#, 8#, 16#

しかしelixirでは、下記の様に扱います。

   iex(12)> 0b101010
    42
    iex(13)> 0o0677
    447
    iex(14)> 0xAE
        174

変数

Erlangでは下記のような特徴があります。

  • 大文字から始まる
  • 変数に値を与えたら、変更できない
  • 変数を消去するf関数が存在する
  • 変更は出来ないが、同値であれば変数に代入する「ふり」はできる

elixirでは、下記が異なって来ます。

  • 変数に再代入が可能
  • ^を使用することで、Erlang同様に再代入の禁止が可能

パターンマッチ

=演算子の動作はパターンマッチとなっています。

iex(15)> one
** (CompileError) iex:15: undefined function one/0

iex(15)> one = 1
1
iex(16)> un = uno = one = 1
1
iex(17)> un
1
iex(18)> uno
1
iex(19)> one
1
iex(20)> two = one + one
2
iex(21)> two = 2
2
iex(22)> two = two + 1
3
iex(23)> two
3
iex(24)> ^two = 3
3
iex(25)> ^two = 4
** (MatchError) no match of right hand side value: 4

基本的に、
左辺の項が変数で束縛されていなかった場合
自動的に右辺の値を変数に束縛します。

比較は結果として成功となります。
変数はメモリに値を保存。

インデックス22でErlangならエラーとなります。
(Erlangが変数を変更出来ないという仕様から)

インデックス25のパターンマッチで
左辺で再代入禁止の^を宣言していて、値3を固定。
その上で、右辺は4でパターンマッチしません。
よってエラーが吐かれてます。

※パターンマッチについての詳細な説明に関しては、下記参照の事
http://elixir-lang.org/getting-started/pattern-matching.html

また、round/truncを使うことで、
小数点から整数を抽出することも可能です。

   iex(25)> round 3.58
    4
    iex(26)> trunc 3.58
    3

アトム

アトムは記述したアトムの名前をそのまま保持しています。
なんかEnumみたい?とか勝手に感じた。

Erlangでは、小文字で宣言。
ダブルクォートで囲ってもアトムとして扱われる。
-> elixirでは、:で始めます。
ダブルクォートも同様に:の後に、
ダブルクォートで囲ってもアトムとなる。

iex(27)> :hello
:hello
iex(28)> :hello == :world
false
iex(29)> :hello == :hello
true
iex(31)> :"hello"
:hello
iex(32)> :hello == :"hello"
true

引用文 アトムはアトム表内で参照されていて、
これはメモリを消費します
(1アトムにつき、32ビットで4バイト、
64ビットで8バイト)
アトム表はガベージコレクトの対象にならないので、
アトムはシステムが落ちるか1048577個のアトムが
宣言されるまで蓄積されていきます。

これはつまり、アトムは動的に生成すべきではないということを意味します。

ブール代数と比較演算子

Erlangは、andorxornotが使用できる。
-> elixirでは、andornotが使用できる。

また、elixirは追加で以下も使用することが可能である。

  • boolean以外で、&&||を使用可能
  • javascriptの様に厳格な比較として===!==を使用可能
  • 大なり、小なりの比較<=>=

型が異なる2つを比較した際は
公式に書いてあるような順序が優先されるとのこと。

number < atom < reference < functions < port < pid < tuple < maps < list < bitstring
   iex(1)> true and false
    false
    iex(2)> false or true
    true
    iex(3)> true && false
    false
    iex(4)> false || true
    true
    iex(5)> not false
    true
    iex(6)> not  (true and true)
    false
    iex(7)> 5 == 5
    true
    iex(8)> 5 === 5
    true
    iex(9)> 5 == 5.0
    true
    iex(10)> 5 === 5.0
    false
    iex(11)> 1 !== true
    true
    iex(12)> 1 >= 1
    true
    iex(13)> 1 > 1
    false
    iex(14)> 1 > false
    false
    iex(15)> 1 > true
    false

タプル

  • {}で表記
  • _は気にしない変数として扱います
  • タプルに対するパターンマッチは、要素の長さが同じ時だけタプルを展開する
  • タプルのひとつ目の要素にアトムを指定するタプルはタグ付きタプル
_について
  • 使わずに捨ててしまう値が来る場所に配置
  • _は常に未束縛と認識
  • パターンマッチではワイルドカードだと思えばいい
   iex(17)> point = {4, 5}
    {4, 5}
    iex(18)> {x, y} = point
    {4, 5}
    iex(19)> x
    4
    iex(20)> y
    5
    iex(21)> {x, _} = point
    {4, 5}
    iex(22)> {_, _} = {4, 5}
    {4, 5}
    iex(23)> temperature = 23.213
    23.213
    iex(24)> term = {:celsius, 23.213}
    {:celsius, 23.213}
    iex(25)> {:kelvin, T} = term
    ** (MatchError) no match of right hand side value: {:celsius, 23.213}

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

リスト

  • 最も使われるデータ構造
  • リストの中の数字の内、一つでも文字として表示できないものがあると、数字のリストとして数字を表示する

-> Erlangでstringがないため、クソと言われる所以となっています。
elixirにはstringが存在するため解消?されてますね?

iex(30)> [1,2,3,{:number, [4,5,6]},5.34,:atom]
[1, 2, 3, {:number, [4, 5, 6]}, 5.34, :atom]
iex(31)> [97,98,99]
'abc'
iex(32)> [97,98,99,4,5,6]
[97, 98, 99, 4, 5, 6]
iex(33)> [233]
[233]

リストでは、結合と削除が可能で、
実行順は右からとなる。

  • 結合 -> ++
  • 削除 -> --
iex(40)> [1,2,3] ++ [4,5]
[1, 2, 3, 4, 5]
iex(41)> [1,2,3,4,5] -- [1,2,3]
[4, 5]
iex(42)> [2,4,2] -- [2,4]
[2]
iex(43)> [1,2,3,4,5] -- [1,3,5]
[2, 4]
組み込み関数(BIF)
  • head -> リストの最初の要素を取得
  • tail -> リストの最後の要素を取得
  • length -> リストの要素数を取得
   iex(50)> hd([1,2,3,4])
    1
    iex(51)> tl([1,2,3,4])
    [2, 3, 4]
        iex(86)> length([1,2,3,4])
        4
コンストラクタ |
   iex(58)> list = [2,3,4]
    [2, 3, 4]
    iex(59)> newList = [1 | list]
    [1, 2, 3, 4]
    iex(60)> [head | tail] = newList
    [1, 2, 3, 4]
    iex(61)> head
    1
    iex(62)> tail
    [2, 3, 4]
    iex(63)> [newHead | newTail] = tail
    [2, 3, 4]
    iex(64)> newHead
    2


    iex(55)> [1 | []]
    [1]
    iex(56)> [2 | [1 | []]]
    [2, 1]
    iex(57)> [3 | [2 | [1 | []]]]
    [3, 2, 1]

リスト内包表記

簡潔に言うと、リストの組み立て、修正を綺麗に短く記述する方法

elixirでの特徴

  • forを使用できる
  • 範囲を表記する際、1..100を使用できる
iex(70)> for n <- [1,2,3,4,5], do: 2 * n + 1
[3, 5, 7, 9, 11]
iex(71)> for n <- [1,2,3,4,5], do: 2 * n
[2, 4, 6, 8, 10]
iex(72)> for n <- 1..5, do: 2 * n
[2, 4, 6, 8, 10]

iex(73)> restaurantMenu = [{:steak, 5.99}, {:beer, 3.99}, {:poutine, 3.50}, {:kitten, 20.99}, {:water, 0.00}]
[steak: 5.99, beer: 3.99, poutine: 3.5, kitten: 20.99, water: 0.0]
iex(74)> for {item, price} <- restaurantMenu, price >= 3, price <= 10, do: { item, price * 1.07}
[steak: 6.409300000000001, beer: 4.2693, poutine: 3.745]

iex(75)> for x <- [1,2], y <- [3,4], do: x+y
[4, 5, 5, 6]

iex(77)> foggyPlaces = for {x, :fog} <- weather, do: x
[:london, :boston]
iex(79)> foggyPlaces = for {x,y} <- weather, y == :fog, do: x
[:london, :boston]

バイナリデータを扱う

使うときに記述しますね。(疲れたなんて言えない)