loading...
gumi TECH Blog

Elixir入門 22: Erlangライブラリ

gumitech profile image gumi TECH ・6 min read

本稿はElixir公式サイトの許諾を得て「Erlang libraries」の解説にもとづき、加筆補正を加えて、Elixirで使えるErlangライブラリついてご説明します。

ElixirはErlangのライブラリが使えます。Erlangライブラリを単にラップするのでなく、Erlangのコードへの直接のインタフェースが備わっているのです。Elixirにはない、よく使われて便利なErlangの機能を採り上げてご紹介します。さらに詳しくはErlang「STDLIB Reference Manual」をご参照ください。

binaryモジュール

Elixir組み込みのStringモジュールは、UTF-8でエンコードされたバイナリを扱います。Erlangのbinaryモジュールは、必ずしもUTF-8エンコーディングでないバイナリを使うときに便利です。String.to_charlist/1はUnicodeのコードポイントを返します。:binary.bin_to_list/1が返すのは生のバイトデータです。

iex> String.to_charlist("π")
[960]
iex> :binary.bin_to_list("π")
[207, 128]

出力のフォーマット

Elixirには、Cなどの言語にあるprintfに当たる関数が備わっていません。けれども、Erlang標準ライブラリの関数:io.format/2:io_lib.format/2が使えます。フォーマットは前者が端末出力、後者はiolistです。書式の定め方はprintfとは異なります。詳しくは:io.format/1の説明をご参照ください。

iex> :io.format("Pi is approximately given by:~10.3f~n", [:math.pi])
Pi is approximately given by:     3.142
:ok
iex> to_string :io_lib.format("Pi is approximately given by:~10.3f~n", [:math.pi])
"Pi is approximately given by:     3.142\n"

なお、Erlangのフォーマット関数を使うとき、Unicodeの扱いには注意しなければなりません。

iex> :io.format("円周率πは約~10.3f~n", [:math.pi])
åå¨çÏã¯ç´
              3.142
:ok
iex> to_string :io_lib.format("円周率πは約:~10.3f~n", [:math.pi])
<<195, 165, 194, 134, 194, 134, 195, 165, 194, 145, 194, 168, 195, 167, 194,
  142, 194, 135, 195, 143, 194, 128, 195, 163, 194, 129, 194, 175, 195, 167,
  194, 180, 194, 132, 58, 32, 32, 32, 32, 32, 51, 46, 49, 52, 50, 10>>

cryptoモジュール

cryptoモジュールには、ハッシュ関数やデジタル署名あるいは暗号化などの機能が含まれています。crypto.hash/2は、第2引数のデータから第1引数のハッシュ関数にもとづいてメッセージダイジェストを求めます(「SHA-256」参照)。また、Base.encode16/2は、バイナリ文字列をbase 16にエンコードする関数です。

iex> Base.encode16(:crypto.hash(:sha256, "Elixir"))
"3315715A7A3AD57428298676C5AE465DADA38D951BDFAC9348A8A31E9C7401CB"

cryptoモジュールは、Erlang標準ライブラリには含まれていません。けれど、Erlangには同梱されています。つまり、使うときにプロジェクトのアプリケーションリストに含めなければなりません。そのためには、mix.exsapplicationにつぎのように書き加えてください(「Project compilation」参照)。

def application do
  [extra_applications: [:crypto]]
end

digraphモジュール

digraphモジュール(およびdigraph_utils)には、頂点と線分で組み立てられる有向グラフ(directed graph)を扱う関数が備わっています。グラフをつくったあと、モジュールのアルゴリズムを用いて、たとえば2頂点またはループ内の最短経路を見つけることができます。

iex> digraph = :digraph.new()
{:digraph, #Reference<0.2617837314.593887233.57913>,
 #Reference<0.2617837314.593887233.57914>,
 #Reference<0.2617837314.593887233.57915>, true}
iex> coords = [{0.0, 0.0}, {3.0, 0.0}, {3.0, 4.0}]
[{0.0, 0.0}, {3.0, 0.0}, {3.0, 4.0}]
iex> [v0, v1, v2] = (for c <- coords, do: :digraph.add_vertex(digraph, c))
[{0.0, 0.0}, {3.0, 0.0}, {3.0, 4.0}]
iex> :digraph.add_edge(digraph, v0, v1)
[:"$e" | 0]
iex> :digraph.add_edge(digraph, v1, v2)
[:"$e" | 1]
iex> :digraph.add_edge(digraph, v0, v2)
[:"$e" | 2]
iex> :digraph.get_short_path(digraph, v0, v2)
[{0.0, 0.0}, {3.0, 4.0}]
  • :digraph.new/0: 空の有向グラフをつくって返します。
  • :digraph.add_vertex/2: 第1引数のグラフに第2引数の頂点が加わります。
  • :digraph.add_edge/3: 第1引数のグラフに第2および第3引数を結ぶ線が加えられます。
  • :digraph.get_short_path/3: 第1引数のグラフの中で、第2引数の頂点から第3引数の頂点に至る最短経路をリストで返します。

Erlangタームストレージ(ETS)

モジュールetsdetsは、大量のデータ構造を扱ってメモリやディスクに格納します。

ETSを使うと、タプルの含まれたテーブルがつくれます。デフォルトでは、ETSのテーブルは保護されています。つまり、所有者だけがテーブルに書き込めるということです。他のプロセスは読み取りのみできます。ETSに備わる機能は、シンプルなデータベースやキーと値の保管、あるいはキャッシュの仕組みなどに使えるでしょう。

etsモジュールの関数は、副作用としてテーブルの状態を変更します。

iex> table = :ets.new(:ets_test, [])
#Reference<0.2617837314.593887233.58208>
iex> :ets.insert(table, {"China", 1_390_080_000})
true
iex> :ets.insert(table, {"USA", 325_890_000})
true
iex> :ets.insert(table, {"Japan", 126_750_000})
true
iex> :ets.i(table)
<1   > {<<"USA">>,325890000}
<2   > {<<"China">>,1390080000}
<3   > {<<"Japan">>,126750000}
EOT  (q)uit (p)Digits (k)ill /Regexp -->
  • :ets.new/2: テーブルをつくって、参照のためのテーブルIDが返されます。引数は名前とオプションのリストです。
  • :ets.insert/2: テーブルにオブジェクトを加えます。
  • :ets.i/1: 端末でテーブルを閲覧します。

待ち状態を終了するには、[q]キーと[enter]を入力してください。

EOT  (q)uit (p)Digits (k)ill /Regexp -->q
:ok

mathモジュール

mathモジュールは、三角関数や指数関数あるいは対数関数などの一般的な数学演算ができます。

iex> deg_to_rad = :math.pi() / 180
0.017453292519943295
iex> sin = :math.sin(60 * deg_to_rad)
0.8660254037844386
iex> sqrt_3 = :math.sqrt(3)
1.7320508075688772
iex> sin == sqrt_3 / 2
true
iex> :math.atan2(sqrt_3, 1) / deg_to_rad
59.99999999999999
iex> x = :math.exp(10)
22026.465794806718
iex> :math.log(x)
10.0

queueモジュール

queueはデータ構造で、FIFO(先入れ先出し)の両端キューを効率よく実装します。

iex> q = :queue.new
{[], []}
iex> q = :queue.in("A", q)
{["A"], []}
iex> q = :queue.in("B", q)
{["B"], ["A"]}
iex> {value, q} = :queue.out(q)
{{:value, "A"}, {[], ["B"]}}
iex> value
{:value, "A"}
iex> {value, q} = :queue.out(q)
{{:value, "B"}, {[], []}}
iex> value
{:value, "B"}
iex> {value, q} = :queue.out(q)
{:empty, {[], []}}
iex> value
:empty
  • :queue.new/0: 空のキューをつくって返します。
  • :queue.in/2: 項目をキューの最後に加え、新たなキューを返します。
  • :queue.out/1: 項目をキューの最初から取り出します。戻り値は{{value, 項目}, キュー}のかたちのタプルです。

randモジュール

randモジュール

ランダム値を返す関数とランダムなシードを設定する関数があります。

iex> :rand.uniform()
0.5094207040603064
iex> _ = :rand.seed(:exs1024, {123, 123534, 345345})
{%{
   jump: #Function<13.15449617/1 in :rand.mk_alg/1>,
   max: 18446744073709551615,
   next: #Function<12.15449617/1 in :rand.mk_alg/1>,
   type: :exs1024
 },
 {[1777391367797874666, 1964529382746821925, 7996041688159811731,
   16797603918550466679, 13239206057622895956, 2190120427146910527,
   18292739386017762693, 7995684206500985125, 1619687243448614582,
   961993414031414042, 10239938031393579756, 12249841489256032092,
   1457887945073169212, 16031477380367994289, 12526413104181201380,
   16202025130717851397], []}}
iex> :rand.uniform()
0.5820506340260994
iex> :rand.uniform(6)
4
  • :rand.uniform/0: 0.0≦x<1.0の範囲で一様分布する乱数xを浮動小数点数で返します。プロセス辞書は更新されます。
  • :rand.uniform/1: 引数の整数N(1≦N)について、0.0≦x<Nの範囲で一様分布する整数の乱数xを返します。プロセス辞書は更新されます。
  • :rand.seed/2: 第1引数のアルゴリズムと第2引数の整数タプルにより、プロセス辞書の乱数生成にシードを与えます。戻り値は初期状態です。

zipとzlibモジュール

zipモジュールを用いると、メモリやディスクのZIPファイルが読み書きできます。また、ファイル情報が取り出せます。つぎのコードは、ZIPファイルの中のファイル数を数えます。

iex(75)> :zip.foldl(fn _, _, _, acc -> acc + 1 end, 0, :binary.bin_to_list("samples.zip"))
{:ok, 70}
  • :zip.foldl/3: 第3引数のアーカイブ内のファイルに対して、第1引数のコールバック関数を呼び出します。第2引数はコールバックに渡される初期値です。コールバック関数が受け取る引数はつぎの4つです。
    • アーカイブ内のファイル名
    • ファイルの情報を返す関数
    • ファイルの中身を返す関数
    • 初期値から集計された値
  • :binary.bin_to_list/1: 引数をバイトのリストにして返します。

zlibモジュールzlibライブラリのAPIで、データの圧縮と解凍を扱います。

iex> song = "
...> The snow glows white on the mountain tonight
...> Not a footprint to be seen
...> A kingdom of isolation
...> And it looks like I'm the queen"
"\nThe snow glows white on the mountain tonight\nNot a footprint to be seen\nA kingdom of isolation\nA
nd it looks like I'm the queen"
iex> compressed = :zlib.compress(song)<<120, 156, 29, 204, 49, 14, 195, 48, 12, 67, 209, 221, 167, 224, 214, 115, 116,
  236, 210, 169, 23, 112, 16, 197, 22, 108, 139, 77, 172, 192, 215, 175, 208,
  145, 15, 248, 76, 159, 42, 152, 198, 133, 210, 185, 38, 86, 85, 23, 208, 224,
  ...>>
iex> byte_size(song)
127
iex> byte_size(compressed)
107
iex> :zlib.uncompress(compressed)
"\nThe snow glows white on the mountain tonight\nNot a footprint to be seen\nA kingdom of isolation\nA
nd it looks like I'm the queen"
  • :zlib.compress/1: zlibヘッダとチェックサムでデータを圧縮します。
  • byte_size/1: ビットストリングを納めるのに必要なバイト数を返します。
  • :zlib.uncompress/1: zlibヘッダーとチェックサムでデータの圧縮を解除します。

Elixir入門もくじ

番外

Posted on by:

gumitech profile

gumi TECH

@gumitech

gumi TECH は、株式会社gumiのエンジニアによる技術記事公開やDrinkupイベントなどの技術者交流を行うアカウントです。 gumi TECH Blog: http://dev.to/gumi / gumi TECH Drinkup: http://gumitech.connpass.com

gumi TECH Blog

株式会社gumiのエンジニアによる技術記事を公開しています。

Discussion

pic
Editor guide