Elixir入門 02: 型の基本
Tags: elixir, webdev, tutorial, programming
Published: 2018-10-02 07:42:08

---
title: Elixir入門 02: 型の基本
published: true
description: Elixir公式サイトの許諾を得て「Basic types」の解説にもとづき、加筆補正を加えてElixirの基本的なデータ型についてご説明します。
tags: #elixir, #webdev, #tutorial, #programming
---

本稿はElixir公式サイトの許諾を得て「[Basic types](https://elixir-lang.org/getting-started/basic-types.html)」の解説にもとづき、加筆補正を加えてElixirの基本的なデータ型についてご説明します。採り上げる型と値の例は、つぎのとおりです(表001)。

#### 表001■型と値の例
| 基本的な型| 値の例 |
|:-------:|:-----:|
| 整数 | 1 |
| 整数(16進数) | 0x1F |
| 小数 | 1.0 |
| 論理値 | true |
| アトム | :atom |
| 文字列 | "elixir" |
| リスト | [1, 2, 3] |
| タプル | {1, 2, 3} |

# 基本的な数値
整数は四則演算できます。

```elixir
iex> (1 + 2) * 3 - 4
5
```

ただし、除算(演算子`/`)は結果が整数であっても、浮動小数点数に変わります。

```elixir
iex> 10 / 2
5.0
```

整数の割り算で、商の整数部を得るのは[`div`関数](https://hexdocs.pm/elixir/Kernel.html#div/2)です。関数の引数はかっこ`()`で囲んでも構いませんし、省くこともできます。なお、整数の割り算の余り(剰余)を求めるのは[`rem`関数](https://hexdocs.pm/elixir/Kernel.html#rem/2)です。

```elixir
iex> div(10, 2)
5
iex> div 10, 2
5
iex> rem 10, 3
1
```

Elixirでは名前のついた関数のかっこ`()`は省けるので、宣言や制御の流れの組み立てが見やすい構文で書けます。

整数には接頭辞をつけて、2進数(`0b`)や8進数(`0o`)あるいは16進数(`0x`)として扱えます。

```elixir
iex> 0b1000
8
iex> 0o10
8
iex> 0xFF
255
```

小数として扱う数値には、小数点をつけます。小数点以下の数値は省けません。端数がないときには、0を添えてください。あるいは、`e`を用いた浮動小数点数の形式で表すこともできます。

```elixir
iex> 1.0
1.0
iex> 3.14e-2
0.0314
```

Elixirの浮動小数点数値は64ビットの倍精度です。

関数[`round`](https://hexdocs.pm/elixir/Kernel.html#round/1)や[`trunc`](https://hexdocs.pm/elixir/Kernel.html#trunc/1)を使えば、小数点以下の四捨五入や切り捨てができます。

```elixir
iex> round(2.718)
3
iex> trunc(2.718)
2
```

# 関数の表記
Elixirでは、関数は名前と受け取る引数の数によって定まります。引数の数は[アリティ](https://ja.wikipedia.org/wiki/%E3%82%A2%E3%83%AA%E3%83%86%E3%82%A3)と呼ばれます。そこで、ドキュメントで関数は「関数名/アリティ」のように表されるのです。たとえば、前出の数値を四捨五入する関数なら`round/1`です。名前が同じでも引数の数が異なれば、別の関数として扱われることになります。

# 論理(ブーリアン)値
Elixirには、`true`と`false`の2値をもつ論理(ブーリアン)型があります。

```elixir
iex> true
true
iex> 1 > 2
false
```

データ型を調べるためには、型に応じたさまざまな関数があります。論理値かどうか確かめるのは[`is_boolean/1`関数](https://hexdocs.pm/elixir/Kernel.html#is_boolean/1)です。`true`か`false`かの論理値を返します。

```elixir
iex> is_boolean(1 > 2)
true
iex> is_boolean(1)
false
```

数値についてはほかにも、関数[`is_integer/1`](https://hexdocs.pm/elixir/Kernel.html#is_integer/1)(整数)や[`is_float/1`](https://hexdocs.pm/elixir/Kernel.html#is_float/1)(浮動小数点数値)さらに[`is_number/1`](https://hexdocs.pm/elixir/Kernel.html#is_number/1)(数値)などが備わっています。

なお、`iex`では`h()`でヘルプが表示されます。たとえば、`is_number/1`関数について知りたいときは、つぎのように打ち込んでください(アリティは省くこともできます)。関数の構文と説明が示されるでしょう(図001)。

```elixir
iex> h is_number/1
```

#### 図001■iexに示された関数の構文
![elixir_01_001.png](https://thepracticaldev.s3.amazonaws.com/i/3bkykd67u2d69l1inv5w.png)


# アトム
アトムは名前がそのまま値となる定数です。言語によっては、シンボルと呼ばれることもあります(たとえば[Ruby](https://docs.ruby-lang.org/ja/latest/class/Symbol.html))。アトムは名前の前にコロン`:`を添えます。

```elixir
iex> :hello
:hello
iex> :hello == :world
false
```

論理値の`true`と`false`は、アトムとして備わっています。値がアトムか、また論理値かは、それぞれ関数[`is_atom/1`](https://hexdocs.pm/elixir/Kernel.html#is_atom/1)と[`is_boolean/1`](https://hexdocs.pm/elixir/Kernel.html#is_boolean/1)で調べられます。

```elixir
iex> true == :true
true
iex> is_atom(false)
true
iex> is_boolean(:false)
true
```

Elixirのモジュール名もアトムです。

```elixir
iex> is_atom(String)
true
```

カスタムのモジュールを定めるとき、名前は大文字ではじめます。たとえ実際にはモジュールがなくても、頭文字が大文字の識別子はアトムとみなされます。

```elixir
iex> is_atom(MyModule)
true
```

また、Erlangのモジュールを参照するときに、アトムが用いられます。たとえば、Erlangには数学的な関数を納めた[mathモジュール](http://erlang.org/doc/man/math.html)が備わっています。これはアトム`:math`として参照できるのです。

```elixir
iex(6)> :math.pow(2, 3)
8.0
iex(7)> is_atom(:math)
true
```


# 文字列
Elixirでは文字列は、ダブルクォーテーション(`""`)にかこんで示します。エンコーディングはUTF-8です。

```elixir
iex> "hello"
"hello"
iex> "拝啓"
"拝啓"
```

アトムは`#{}`でかこんで文字列補間することにより、文字列に含めることができます(「[Elixir の危険な構文、atom文字列補間](https://qiita.com/melpon/items/63bcc6f26200c4081c1f)」参照)。

```elixir
iex> "hello #{:world}"
"hello world"
```

文字列には改行が含められます。また、エスケープシーケンス(`\n`)で加えてもかまいません。

```elixir
iex> "hello
...> world"
"hello\nworld"
iex> "hello\nworld"
"hello\nworld"
```

文字列を出力するには、`IO`モジュールの[`IO.puts/2`関数](https://hexdocs.pm/elixir/IO.html#puts/2)を使います。加えて示されるアトム`:ok`は、正しく出力できたときの戻り値です。

```elixir
iex> IO.puts "hello\nworld"
hello
world
:ok
```

Elixirは文字列をバイトシーケンスのバイナリとして扱います。バイナリであることを確かめる関数は[`is_binary/1`](https://hexdocs.pm/elixir/1.0.5/Kernel.html#is_binary/1)です。

```elixir
iex> is_binary("hello")
true
```

[`byte_size/1`関数](https://hexdocs.pm/elixir/1.0.5/Kernel.html#byte_size/1)を用いれば、バイト数が調べられます。

```elixir
iex> byte_size("hello")
5
iex> byte_size("拝啓")
6
```

UTF-8では、バイト数は必ずしも文字数を表しません。文字数を得るために使うのは[`String.length/1`関数](https://hexdocs.pm/elixir/String.html#length/1)です。

```elixir
iex(16)> String.length("hello")
5
iex(17)> String.length("拝啓")
2
```


# 無名関数
Elixirでは、関数は他の関数に引数として渡したり、戻り値として返すことができる「[第一級関数](https://ja.wikipedia.org/wiki/%E7%AC%AC%E4%B8%80%E7%B4%9A%E9%96%A2%E6%95%B0)」(first-class function)です。そのような場合、関数に名前をつけずに定めることが少なくありません。あとからその関数を呼び出したいときは、変数に納めます。

関数はキーワード`fn`ではじめ、`end`で閉じます。引数はかっこ`()`の中にカンマ(`,`)区切りで与え、つづけて添えた`->`のあとに本体の文を書きます。なお、引数をかこむ`()`は省いてもかまいません。正しく定められると、`#Function<...>`の記述が示されます(`<>`内は環境によって異なります)。

```elixir
iex> sum = fn (a, b) -> a + b end
#Function<12.99386804/2 in :erl_eval.expr/5>
```

呼び出すときは、関数が納められた変数を参照し、ドット(`.`)につづけてかっこ`()`に引数をカンマ区切りで渡します。なお、この場合の`()`は省けません。

```elixir
iex> sum.(1, 2)
3
```

変数のあとにドット(`.`)を添えるのは、名前のある関数でないことを示すためです。Elixirでは、名前のある関数と無名関数とを区別します。

変数に関数が納められているかどうかは、[`is_function/1`関数](https://hexdocs.pm/elixir/Kernel.html#is_function/1)でたしかめられます。

```elixir
iex> is_function(sum)
true
```

さらに、アリティの数まで調べられるのが[`is_function/2`関数](https://hexdocs.pm/elixir/Kernel.html#is_function/2)です。アリティ数を第2引数に渡します。

```elixir
iex> is_function(sum, 1)
false
iex> is_function(sum, 2)
true
```

無名関数は[キャプチャ演算子`&`](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#&/1)を用いて、短く書き表すことができます(「[Function capturing](https://elixir-lang.org/getting-started/modules-and-functions.html#function-capturing)」参照)。`&`に1からの整数連番を添えて示されるのが引数です。

```elixir
iex> sum = &(&1 + &2)
&:erlang.+/2
iex> sum.(3, 4)
7
```

無名関数は[クロージャ](https://ja.wikipedia.org/wiki/%E3%82%AF%E3%83%AD%E3%83%BC%E3%82%B8%E3%83%A3)です。みずからの定められたスコープの中で参照を解決します。すでに変数に納めた関数を、別の関数から参照して使うこともできます。

```elixir
iex> double = fn (a) -> sum.(a, a) end
#Function<6.99386804/1 in :erl_eval.expr/5>
iex> double.(2)
4
```

無名関数は変数に納めなくても、ただちに呼び出すことはできます。なお、関数内で割り当てられた変数が、外の環境に影響を与えることはありません。

```elixir
iex> x = 0
0
iex> (fn -> x = 1 end).()
1
iex> x
0
```


# リスト
いくつもの値をひとつにまとめたのがリストです。角かっこ`[]`の中に値(要素)をカンマ区切りで与えます。値はどのような型でもかまいません。リストの長さ(要素の数)は、[`length/1`関数](https://hexdocs.pm/elixir/Kernel.html#length/1)で調べられます。

```elixir
iex> list = [3.14, :pie, true, "Apple"]
[3.14, :pie, true, "Apple"]
iex> length(list)
4
```

演算子[++/2](https://hexdocs.pm/elixir/Kernel.html#++/2)や[--/2](https://hexdocs.pm/elixir/Kernel.html#--/2)を用いて、別のリストの要素を加えたり、差し引いたりすることができます。

```elixir
iex> list ++ ["Cherry"]
[3.14, :pie, true, "Apple", "Cherry"]
iex> ["π"] ++ list
["π", 3.14, :pie, true, "Apple"]
iex> list -- [true, false]
[3.14, :pie, "Apple"]
```

重複した値が含まれるリストから複数の同じ値を差し引くときは、左にある値から順に除かれます。このとき差し引く値は、厳密な等価でマッチしなければなりません(「[Elixir入門 03: 演算子の基本]()」「比較」参照)。

```elixir
iex> [1, 1, 2, 1, 2, 3, 1, 2, 3, 4] -- [4, 1, 3, 1, 2]
[1, 2, 1, 2, 3]
```

リスト演算子は、参照したリストそのものの中身は変えません。返されるのは新たなリストです。Elixirは「[イミュータブル](https://ja.wikipedia.org/wiki/%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB)」(immutable)なデータ構造をもちます。データが書き替えられることはなく、別のデータに変換することしかできません。それにより、クリーンなコードが保てるのです。

リストを扱うときは、たびたびヘッド(head)とテイル(tail)を取り出します。ヘッドが最初の要素、テイルは残りの要素を納めたリストです。それぞれ、関数[hd/1](https://hexdocs.pm/elixir/Kernel.html#hd/1)と[tl/1](https://hexdocs.pm/elixir/Kernel.html#tl/1)で得られます。

```elixir
iex> hd(list)
3.14
iex> tl(list)
[:pie, true, "Apple"]
```

リストが空(長さ0)のときは、引数エラー(`ArgumentError`)になります。

```elixir
iex> hd([])
** (ArgumentError) argument error
    :erlang.hd([])
iex> tl([1])
[]
iex> tl([])
** (ArgumentError) argument error
    :erlang.tl([])
```

`|`演算子を用いたパターンマッチングにより、ヘッドとテイルをふたつの変数に与えることもできます(「[Pattern matching](https://elixir-lang.org/getting-started/pattern-matching.html#pattern-matching-1)」参照)。

```elixir
iex> [head | tail] = list
[3.14, :pie, true, "Apple"]
iex> head
3.14
iex> tail
[:pie, true, "Apple"]
```

整数のリストをつくったとき、シングルクォーテーション(`''`)にかこわれた整数でない値で返されることがあります。

```elixir
iex> [10, 11, 12]
'\n\v\f'
iex> [104, 101, 108, 108, 111]
'hello'
```

Elixirは出力できるASCIIコードのリストを、コードに対応した文字リスト(charlist)として示すのです。charlistはErlangのコードとのインタフェースでよく用いられます(「[Charlists](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html#charlists)」参照)。シングルクォーテーションにかこわれたcharlistの情報は、iexの[`i/1`関数](https://hexdocs.pm/iex/IEx.Helpers.html#i/1)で調べられます。

```elixir
iex> i('hello')
Term
  'hello'
Data type
  List
Description
  This is a list of integers that is printed as a sequence of characters
  delimited by single quotes because all the integers in it represent valid
  ASCII characters. Conventionally, such lists of integers are referred to
  as "charlists" (more precisely, a charlist is a list of Unicode codepoints,
  and ASCII is a subset of Unicode).
Raw representation
  [104, 101, 108, 108, 111]
Reference modules
  List
Implemented protocols
  IEx.Info, Inspect, String.Chars, List.Chars, Collectable, Enumerable
```

Elixierでは、同じ文字列でも、シングルクォーテーションとダブルクォーテーションのどちらでかこうかにより扱いが異なります。ふたつのデータが、それぞれ別の型だからです。

```elixir
iex> 'hello' == "hello"
false
iex> i("hello")
Term
  "hello"
Data type
  BitString
Byte size
  5
Description
  This is a string: a UTF-8 encoded binary. It's printed surrounded by
  "double quotes" because all UTF-8 encoded codepoints in it are printable.
Raw representation
  <<104, 101, 108, 108, 111>>
Reference modules
  String, :binary
Implemented protocols
  IEx.Info, Inspect, String.Chars, List.Chars, Collectable
```


# タプル
タプルもリストと同じく、複数の値がまとめて納められます。値はどのような型でもかまいません。波かっこ`{}`の中に値(要素)をカンマ区切りで与えます。長さ(要素数)を調べる関数は[tuple_size/1](https://hexdocs.pm/elixir/Kernel.html#tuple_size/1)です。

```elixir
iex(5)> tuple = {3.14, :pie, true, "Apple"}
{3.14, :pie, true, "Apple"}
iex(6)> tuple_size(tuple)
4
```

タプルは、要素をメモリに連続して納めます。そのため、長さはリストより速く得られます。また[`elem/2`関数](https://hexdocs.pm/elixir/Kernel.html#elem/2)を用いて、0からはじまる整数連番のインデックスで、要素を取り出すこともできるのです。

```elixir
iex> elem(tuple, 3)
"Apple"
```

[`put_elem/3`関数](https://hexdocs.pm/elixir/Kernel.html#put_elem/3)で、指定したインデックスの値を置き替えることもできます。ただし、リストと同じく、タプルもイミュータブルです。操作をして返されるのは新たなタプルで、参照もとのデータは変わりません。

```elixir
iex> put_elem(tuple, 3, "Cherry")
{3.14, :pie, true, "Cherry"}
iex> tuple
{3.14, :pie, true, "Apple"}
```


# リストとタプルの違い
リストもタプルもデータの型を問わず、値がいくつでも要素として納められました。では、ふたつの違いは何でしょう。

リストは「[連結リスト](https://ja.wikipedia.org/wiki/%E9%80%A3%E7%B5%90%E3%83%AA%E3%82%B9%E3%83%88)」です。要素は値とつぎの要素への参照をもちます。そのためリストの長さは、要素をはじめから順に最後までたどって数えないとわからないのです。

また、別のリストを加えるときも、`++/2`演算子の左オペランドのリストが長いほど遅くなることになります。左オペランドのリスト要素の最後に、右オペランドのリスト最初の要素の参照を与える操作だからです。

```elixir
iex> list = [1, 2, 3]
# 左オペランドの長さが1なので速い
iex> [0] ++ list
[0, 1, 2, 3]
# 左オペランドの長さが3なので遅い
iex> list ++ [4]
[1, 2, 3, 4]
```

タプルは、値を連続してメモリに納めます。そのため、サイズを得たり、インデックスで値を取り出す操作も速いのです。けれども、要素を加えたり、書き替えたりする場合は、メモリに新たなタプルをつくることになるので、負荷が高くなってしまいます。

```elixir
iex> tuple = {:a, :b, :c, :d}
iex> put_elem(tuple, 2, :e)
{:a, :b, :e, :d}
```

これは、タプルの構造のお話でした。中の要素については、タプルもリストもデータは共有します。たとえば、ひとつの要素を書き替えたことでつくられる新しいタプルは、更新された要素を除いて、古いタプルとデータは共有するのです。プログラムが費やすメモリはその分抑えられます。イミュータブルな言語の恩恵といえるでしょう。

パフォーマンスの性質によりデータ構造の使い方が決まります。タプルがよく用いられるのは、関数の戻り値として情報を返すときです。たとえば、[`File.read/1`関数](https://hexdocs.pm/elixir/File.html#read/1)は、ファイル読み込みの結果と内容をタプルで返します。

```elixir
iex> File.read("path/to/existing/file")
{:ok, "... ファイルの中身 ..."}
iex> File.read("path/to/unknown/file")
{:error, :enoent}
```

関数の引数に渡したパスが読み込めれば、戻り値のタプルのひとつ目の要素はアトム`:ok`で、ふたつ目がファイルの中身です。読み込めなかったときは、戻り値の第1要素はアトム`:error`で、第2要素がエラー内容を示します。

また、タプルは`elem/2`関数で、指定したインデックスの要素が得られます。リストにはこの機能が組み込まれていません。

```elixir
iex> tuple = {:ok, "hello"}
{:ok, "hello"}
iex> elem(tuple, 1)
"hello"
```

データ構造の中の要素を数える関数について、Elixirは名前のつけ方を決めています(表002)。あらかじめ計算された値を返すのが`size`のつく関数です。数えて返す関数には`length`がつきます。長さを調べて返すので、数が増えるほど処理は遅くなるのです。

#### 表002■sizeとlengthの用語の違い
| 使われる語| 値の求め方 | 処理 |
|:-------:|:----------|:----|
| `size` | あらかじめ計算されている | 速い | 
| `length` | 数えて返す | 遅い | 

つぎの4つの関数をすでにご紹介しました。同じ文字列に対する操作でも、バイト数を得る`byte_size/1`は速い処理です。ところが、Unicodeの文字数を調べる`String.length/1`は、文字列が長くなるほど処理には時間がかかってしまいます。

- `byte_size/1`: 文字列のバイト数
- `tuple_size/1`: タプルの大きさ
- `length/1`: リストの長さ
- `String.length/1`: 文字列の文字数


#### Elixir入門もくじ
- [Elixir入門 01: コードを書いて試してみる](https://dev.to/gumi/elixir-01--2585)
- Elixir入門 02: 型の基本
- [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ライブラリ](https://dev.to/gumi/elixir-22-erlang-2492)
- [Elixir入門 23: つぎのステップ](https://dev.to/gumi/elixir-23--50ik)

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