Elixir入門 22: Erlangライブラリ
Tags: elixir, webdev, tutorial, programming
Published: 2019-02-19 01:42:26

---
title: Elixir入門 22: Erlangライブラリ
published: true
description: Elixir公式サイトの許諾を得て「Erlang libraries」の解説にもとづき、加筆補正を加えて、Elixirで使えるErlangライブラリついてご説明します。
tags: #elixir, #webdev, #tutorial, #programming
---

本稿はElixir公式サイトの許諾を得て「[Erlang libraries](https://elixir-lang.org/getting-started/erlang-libraries.html)」の解説にもとづき、加筆補正を加えて、Elixirで使えるErlangライブラリついてご説明します。

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


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

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


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

```elixir
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の扱いには注意しなければなりません。

```elixir
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`モジュール](http://erlang.org/doc/man/crypto.html)には、ハッシュ関数やデジタル署名あるいは暗号化などの機能が含まれています。[`crypto.hash/2`](http://erlang.org/doc/man/crypto.html#hash-2)は、第2引数のデータから第1引数のハッシュ関数にもとづいてメッセージダイジェストを求めます(「[SHA-256](http://e-words.jp/w/SHA-256.html)」参照)。また、[`Base.encode16/2`](https://hexdocs.pm/elixir/Base.html#encode16/2)は、バイナリ文字列をbase 16にエンコードする関数です。

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

`crypto`モジュールは、Erlang標準ライブラリには含まれていません。けれど、Erlangには同梱されています。つまり、使うときにプロジェクトのアプリケーションリストに含めなければなりません。そのためには、`mix.exs`の`application `につぎのように書き加えてください(「[Project compilation](https://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html#project-compilation)」参照)。

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


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

```elixir
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`](http://erlang.org/doc/man/digraph.html#new-0): 空の有向グラフをつくって返します。
- [`:digraph.add_vertex/2`](http://erlang.org/doc/man/digraph.html#add_vertex-2): 第1引数のグラフに第2引数の頂点が加わります。
- [`:digraph.add_edge/3`](http://erlang.org/doc/man/digraph.html#add_edge-3): 第1引数のグラフに第2および第3引数を結ぶ線が加えられます。
- [`:digraph.get_short_path/3`](http://erlang.org/doc/man/digraph.html#get_short_path-3): 第1引数のグラフの中で、第2引数の頂点から第3引数の頂点に至る最短経路をリストで返します。


# Erlangタームストレージ(ETS)
モジュール[`ets`](http://erlang.org/doc/man/ets.html)と[`dets`](http://erlang.org/doc/man/dets.html)は、大量のデータ構造を扱ってメモリやディスクに格納します。

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

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

```elixir
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`](http://erlang.org/doc/man/ets.html#new-2): テーブルをつくって、参照のためのテーブルIDが返されます。引数は名前とオプションのリストです。
- [`:ets.insert/2`](http://erlang.org/doc/man/ets.html#insert-2): テーブルにオブジェクトを加えます。
- [`:ets.i/1`](http://erlang.org/doc/man/ets.html#i-1): 端末でテーブルを閲覧します。

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

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


# mathモジュール
[`math`モジュール](http://erlang.org/doc/man/math.html)は、三角関数や指数関数あるいは対数関数などの一般的な数学演算ができます。

```elixir
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
```

- [`:math.pi/0`](http://erlang.org/doc/man/math.html#pi-0): 円周率 π
- [`:math.sin/1`](http://erlang.org/doc/man/math.html#sin-1): 三角関数 sinθ (θはラジアン)
- [`:math.sqrt/1`](http://erlang.org/doc/man/math.html#sqrt-1): 平方根 √n
- [`:math.atan2/2`](http://erlang.org/doc/man/math.html#atan2-2): atan2(y, x)で原点と座標(x, y)を結ぶ線分がx軸正方向となす角度をラジアンで返します。
- [`:math.exp/1`](http://erlang.org/doc/man/math.html#exp-1): 指数関数 e^n
- [`:math.log/1`](http://erlang.org/doc/man/math.html#log-1): 自然対数 ln n


# queueモジュール
[`queue`](http://erlang.org/doc/man/queue.html)はデータ構造で、[FIFO](https://www.weblio.jp/content/%E3%83%95%E3%82%A3%E3%83%95%E3%82%A9)(先入れ先出し)の[両端キュー](https://ja.wikipedia.org/wiki/%E4%B8%A1%E7%AB%AF%E3%82%AD%E3%83%A5%E3%83%BC)を効率よく実装します。

```elixir
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`](http://erlang.org/doc/man/queue.html#new-0): 空のキューをつくって返します。
- [`:queue.in/2`](http://erlang.org/doc/man/queue.html#in-2): 項目をキューの最後に加え、新たなキューを返します。
- [`:queue.out/1`](http://erlang.org/doc/man/queue.html#out-1): 項目をキューの最初から取り出します。戻り値は`{{value, 項目}, キュー}`のかたちのタプルです。



# randモジュール
[`rand`モジュール](http://erlang.org/doc/man/rand.html)

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

```elm
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`](http://erlang.org/doc/man/rand.html#uniform-0): 0.0≦x<1.0の範囲で[一様分布](https://ja.wikipedia.org/wiki/%E4%B8%80%E6%A7%98%E5%88%86%E5%B8%83)する乱数xを浮動小数点数で返します。プロセス辞書は更新されます。
- [`:rand.uniform/1`](http://erlang.org/doc/man/rand.html#uniform-1): 引数の整数N(1≦N)について、0.0≦x<Nの範囲で[一様分布](https://ja.wikipedia.org/wiki/%E4%B8%80%E6%A7%98%E5%88%86%E5%B8%83)する整数の乱数xを返します。プロセス辞書は更新されます。
- [`:rand.seed/2`](http://erlang.org/doc/man/rand.html#seed-2): 第1引数の[アルゴリズム](http://erlang.org/doc/man/rand.html#type-builtin_alg)と第2引数の整数タプルにより、プロセス辞書の乱数生成にシードを与えます。戻り値は初期状態です。


# zipとzlibモジュール
[`zip`モジュール](http://erlang.org/doc/man/zip.html)を用いると、メモリやディスクのZIPファイルが読み書きできます。また、ファイル情報が取り出せます。つぎのコードは、ZIPファイルの中のファイル数を数えます。

```elixir
iex(75)> :zip.foldl(fn _, _, _, acc -> acc + 1 end, 0, :binary.bin_to_list("samples.zip"))
{:ok, 70}
```

- [`:zip.foldl/3`](http://erlang.org/doc/man/zip.html#foldl-3): 第3引数のアーカイブ内のファイルに対して、第1引数のコールバック関数を呼び出します。第2引数はコールバックに渡される初期値です。コールバック関数が受け取る引数はつぎの4つです。
    - アーカイブ内のファイル名
    - ファイルの情報を返す関数
    - ファイルの中身を返す関数
    - 初期値から集計された値
- [`:binary.bin_to_list/1`](http://erlang.org/doc/man/binary.html#bin_to_list-1): 引数をバイトのリストにして返します。

[`zlib`モジュール](The zlib module)は[zlibライブラリ](http://www.zlib.net/)のAPIで、データの圧縮と解凍を扱います。

```elixir
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`](http://erlang.org/doc/man/zlib.html#compress-1): zlibヘッダとチェックサムでデータを圧縮します。
- [`byte_size/1`](https://hexdocs.pm/elixir/Kernel.html#byte_size/1): ビットストリングを納めるのに必要なバイト数を返します。
- [`:zlib.uncompress/1`](http://erlang.org/doc/man/zlib.html#uncompress-1): zlibヘッダーとチェックサムでデータの圧縮を解除します。


#### Elixir入門もくじ
- [Elixir入門 01: コードを書いて試してみる](https://dev.to/gumi/elixir-01--2585)
- [Elixir入門 02: 型の基本](https://dev.to/gumi/elixir-02--30n1)
- [Elixir入門 03: 演算子の基本](https://dev.to/gumi/elixir-03--33im)
- [Elixir入門 04: パターンマッチング](https://dev.to/gumi/elixir-04--1346)
- [Elixir入門 05: 条件 - case/cond/if](https://dev.to/gumi/elixir-05----casecondif-60o)
- [Elixir入門 06: バイナリと文字列および文字リスト](https://dev.to/gumi/elixir-06--35na)
- [Elixir入門 07: キーワードリストとマップ](https://dev.to/gumi/elixir-07--39hi)
- [Elixir入門 08: モジュールと関数](https://dev.to/gumi/elixir-08--1c4c)
- [Elixir入門 09: 再帰](https://dev.to/gumi/elixir-09--1a0p)
- [Elixir入門 10: EnumとStream](https://dev.to/gumi/elixir-10-enumstream-4fpb)
- [Elixir入門 11: プロセス](https://dev.to/gumi/elixir-11--2mia)
- [Elixir入門 12: 入出力とファイルシステム](https://dev.to/gumi/elixir-12--4og6)
- [Elixir入門 13: aliasとrequireおよびimport](https://dev.to/gumi/elixir-13-aliasrequireimport-55c1)
- [Elixir入門 14: モジュールの属性](https://dev.to/gumi/elixir-14--3511)
- [Elixir入門 15: 構造体](https://dev.to/gumi/elixir-15--4f43)
- [Elixir入門 16: プロトコル](https://dev.to/gumi/elixir-16--lif)
- [Elixir入門 17: 内包表記](https://dev.to/gumi/elixir-17--5gci)
- [Elixir入門 18: シギル](https://dev.to/gumi/elixir-18--5791)
- [Elixir入門 19: tryとcatchおよびrescue](https://dev.to/gumi/elixir-19-trycatchrescue-50i8)
- [Elixir入門 20: 型の仕様とビヘイビア](https://dev.to/gumi/elixir-20--j50)
- [Elixir入門 21: デバッグ](https://dev.to/gumi/elixir-21--21a1)
- Elixir入門 22: Erlangライブラリ
- [Elixir入門 23: つぎのステップ](https://dev.to/gumi/elixir-23--50ik)

##### 番外
- [Elixir入門: Plugを使うには](https://dev.to/gumi/elixir-plug-40lb)