Elixir 基礎文法最速マスター

ErlangVMに載っている言語に関する話題やそうでない話題でわいわいやる集まり[Sapporo.beam](http://sapporo-beam.github.io/)の時間を使って，
[Ruby基礎文法最速マスター](http://route477.net/d/?date=20100125) を参考に Elixir 基礎文法を書きました．

- Ruby が得意なところ
- Ruby と Elixir の似ているところ
- Elxir が得意なところ

という観点のうち，以下の紹介では 「Ruby と Elixir の似ているところ」を主に紹介していきます．

ここがおかしいとかわからないところがあればコメント歓迎いたします．お気軽にどうぞ．

# 基礎

## インタラクティブ (iex)

iex を使うと，Elixir のプログラムを簡単に練習することができます．

```shell-session
/Users/niku% iex
Interactive Elixir (1.0.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> IO.puts "hello"
hello
:ok
iex(2)> 1 + 1
2
```

以下の説明は，iex を起動して，自分で試しながら読むと習得が早いと思います．

NOTE: 入力に失敗してよくわからなくなったとき，Ruby の irb だと Ctrl-C を連打して入力をキャンセルすると思います．一方 Elixir の iex で Ctrl-C を連打すると iex が終了してしまうので，キーボードから `#iex:break` と打ち込みましょう．これで入力をキャンセルできます．詳しくは [IEx](https://hexdocs.pm/iex/IEx.html#module-expressions-in-iex) の Expressions in IEx に書いてあります．

## 表示 (IO.write/1, IO.puts/1, IO.inspect/1)

Elixir の関数は「モジュール名」「関数名」「引数の数」で一意になるので

* モジュール名 :: IO
* 関数名 :: write
* 引数の数 :: 1

を組み合わせて `IO.write/1` のように表します．

```elixir
IO.write "foo" # => 改行なし
IO.puts "foo"  # => 改行あり
IO.inspect 123 # => デバッグ用
```

Ruby でいうところの「p デバッグ」は，Elixir では「IO.inspect デバッグ」になります．

## 変数の宣言

Elixir には変数宣言がありせん．また変数の型もないので、一つの変数にいろいろなクラスのオブジェクトを代入することができます。

```elixir
a = 123
a = "abc" # => a を上書きする
```

Elixir では変数に `^` をつけると再代入を防止できます．

```elixir
a = 123
^a = "abc" # => ** (MatchError) no match of right hand side value: "abc"
```

NOTE: 便宜上，ここでは代入 assign と呼んでいますが，実際には割り当て bind です．

オブジェクト指向言語ではないのでインスタンス変数はありませんが，
モジュールの中で `@変数名 値` とすることでモジュール内で共通して値を利用できます．ただし値の変更はできません．

```elixir
defmodule Foo do
  @val "hoge"
  def x do
    @val
  end
end

Foo.x # => "hoge"
```

## コメント

`#` 以降はコメントになります．

```elixir
# コメント
```

## スクリプトの実行

Elixir はコンパイルすることもできますが，しなくても実行できます．コンパイルするソースコードは `.ex`，コンパイルしないで実行するソースコードは `.exs` をつけるならわしのようです．

スクリプトの実行は `elixir ファイル名` で行います．

```
/Users/niku% echo 'defmodule My do; IO.puts("Hello World"); end' > hello.exs
/Users/niku% elixir hello.exs
Hello World
```

# 数値

整数や小数が使えます．足し算，引き算，割り算などの計算は [Kernel](https://hexdocs.pm/elixir/Kernel.html) にあります．その他整数なら [Integer](https://hexdocs.pm/elixir/Integer.html)，小数なら [Float](https://hexdocs.pm/elixir/Float.html) をみるとできることが大体わかります．

```elixir
num = 1
num = 1.234
num = 100_000_000
```

## 四則演算

```elixir
num = 1 + 1
num = 1 - 1
num = 1 * 2
num = 5 / 2     # => 2.5 (Ruby とは異なり整数同士の割り算でも小数になる)
num = 5.0 / 2   # => 2.5
num = rem(5, 2) # => 5 % 2 という記法はない
```

## インクリメントとデクリメント

Elixir にはありません．Ruby の `+=` や `-=` という記法もありません．`x = x + 1` などのように書いてください．私見ですがあんまり再代入を使わない方がいいような言語に見えますのでインクリメントやデクリメントしたくなったら立ち止まって本当は何をしたいのか考えてみた方がいいんじゃないかなあ．

# 文字列

文字列はシングルクォート `'` やダブルクォート `"` で囲みます．
Elixir ではシングルクォートで囲った文字とダブルクォートで囲った文字は異なる性質を持ちます．ほとんど全ての場合ダブルクォートで囲った文字を使います．最初はシングルクォートのことを忘れましょう．どちらのクォートの中でも `\t` （タブ）や `\n` （改行）などの特殊文字を利用することができ，「`#{}`」を使って任意の式を展開することができます．

まずは [String](https://hexdocs.pm/elixir/String.html) を眺めるとできることがわかるでしょう．そこに無い場合は [Regex](https://hexdocs.pm/elixir/Regex.html) をみるとあるかもしれません．その他文字列の結合などいくつかは [Kernel](https://hexdocs.pm/elixir/Kernel.html) にあります．

```elixir
str1 = 'abc'
str2 = "def"
str3 = "a\tbc\n"
str4 = "#{str1} def" # => "abc def"
```

シングルクォートとダブルクォートの違いについて詳しく知りたければ Getting Started の [Binaries, strings, and charlists](https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html) を見るとわかるでしょう．

## 文字列操作

結合

```elixir
str1 = "aaa" <> "bbb"
str2 = Enum.join(["aaa", "bbb", "ccc"], ",")
```

分割

```elixir
record = String.split("aaa,bbb,ccc", ~r/,/)
```

長さ

```elixir
length = String.length("abcdef")
```

切り出し

```elixir
substr = String.slice("abcd", 0, 2) # => "ab" (0 番目から 2 文字)
substr = String.slice("abcd", 0..1) # => "ab" (範囲 0 から 1 まで)
```

検索

```elixir
[{idx, _}] = Regex.run(~r/bc/, "abcd", return: :index)
idx # => 1
```

# リスト

リストは `[]` を使います．

リストや後述するマップで共通してできることは [Enum](https://hexdocs.pm/elixir/Enum.html) に書いてあります．まずはここを探してみましょう．リストでできてマップでできないようなことは [List](https://hexdocs.pm/elixir/List.html) に書いてあります `Enum` にない場合はこちらをみてみましょう．

```elixir
ary = [100, 200, 300]
```

## 要素の参照と代入

参照

```elixir
a = Enum.at(ary, 0) # => 100
b = Enum.at(ary, 1) # => 200
```

Ruby と違い既にあるリスト(配列)への挿入はできません．新しいリストを作って返すことはできます．

```elixir
List.insert_at(ary, 0, 1) # => [1, 100, 200, 300]
List.insert_at(ary, 1, 2) # => [100, 2, 200, 300]
ary                       # => [100, 200, 300]
```

## 要素の個数

```elixir
n = length(ary)
n = Enum.count(ary) # List に対しては同じ動作になりますが，動作が異なることもあります
```

## リストの操作

パターンマッチを使うことが多いです．
リストのデータ構造上先頭に対して行うのは簡単なのですが，末尾に対して行うのは（コンピュータが）大変なのでパフォーマンス上おすすめできません．

```elixir
ary = [1, 2, 3]
[a|tail] = ary
a                          # => 1
tail                       # => [2, 3]
[5|ary]                    # => [5, 1, 2, 3]
List.last(ary)             # => 3
List.insert_at(ary, -1, 9) # => [1, 2, 3, 9]
ary                        # => [1, 2, 3]
```

パターンマッチについては [Pattern matching](https://elixir-lang.org/getting-started/pattern-matching.html) を読むとよくわかります．

# マップ

マップは `%{}` を使います．

前述のリストやマップで共通してできることは [Enum](https://hexdocs.pm/elixir/Enum.html) に書いてあります．まずはここを探してみましょう．
マップのように「キー」と「値」をペアで持つデータ構造でしかできないようなことは [Map](https://hexdocs.pm/elixir/Map.html) に書いてあります．

マップや，ここでは紹介しませんでしたがマップ風のデータ構造を持つキーワードリストについて詳しく知りたいときは [Keyword lists and maps](https://elixir-lang.org/getting-started/keywords-and-maps.html) を見てみましょう．

```elixir
map = %{a: 1, b: 2}
```

## 要素の参照と代入

```elixir
map[:a] # => 1
map[:b] # => 2
```

Ruby と違い既にあるマップ(ハッシュ)への挿入はできません．新しいマップを作って返すことはできます．

```elixir
Map.put(map, :c, 5) # => %{a: 1, b: 2, c: 5}
Map.put(map, :d, 7) # => %{a: 1, b: 2, d: 7}
map                 # => %{a: 1, b: 2}
```

## マップの操作

キーの取得

```elixir
Map.keys(map) # => [:a, :b]
```

値の取得

```elixir
Map.values(map) # => [1, 2]
```

キーの存在確認

```elixir
Map.has_key?(map, :a) # => true
```

マップのペアの削除

```elixir
Map.delete(map, :a) # => %{b: 2}
```

# 制御文

Elixir でも条件文で偽とみなされるのは `false` と `nil` のみで，それ以外のデータは全て真とみなされます．

if文

```elixir
if a == 1 do
  # ...
else
  # ...
end
```

ループは再帰で表現します．while 文は用意されていません．

```elixir
defmodule My do
  def loop(0) do
    IO.puts "done"
  end

  def loop(times) do
    IO.puts "#{times} times remaining"
    loop(times - 1)
  end
end

My.loop(5)
# => 5 times remaining
# => 4 times remaining
# => 3 times remaining
# => 2 times remaining
# => 1 times remaining
# => done
```

Elixir の再帰について知りたければ [Recursion](https://elixir-lang.org/getting-started/recursion.html) を読むとよいでしょう．

`Enum#each` と無名関数を使った繰り返し

```elixir
Enum.each([1, 2, 3], fn(i) ->    # fn(arg) -> ... end のことを無名関数と呼びます
  IO.puts(i)                     # Enum.each/2 は第一引数の各要素を第二引数に渡して実行してくれます
end)                             # 1, 2, 3 と順に表示します

Enum.each([1, 2, 3], &IO.puts/1) # fn(i) -> IO.puts(i) end を省略して &IO.puts/1 と書くこともできます
```

## その他の繰り返し

`Enum` モジュールには繰り返しを行う便利な関数がたくさん定義されています．

条件に合うものだけを選ぶ

```elixir
Enum.filter([1, 2, 3, 4, 5], &rem(&1, 2) == 0) # => [2, 4]
```

条件に合うものを除く

```elixir
Enum.reject([1, 2, 3, 4, 5], &rem(&1, 2) == 0) # => [1, 3, 5]
```

条件に合う最初のものを返す

```elixir
Enum.find([1, 2, 3, 4, 5], &rem(&1, 2) == 0)   # => 2
```

等しい値があるか調べる

```elixir
Enum.member?([1, 2, 3, 4, 5], 3)               # => true
```

条件に合うものがあるか調べる

```elixir
Enum.any?([1, 2, 3, 4, 5], &rem(&1, 2) == 0)   # => true
```

全ての要素が条件に合うかを調べる

```elixir
Enum.all?([1, 2, 3, 4, 5], &rem(&1, 2) == 0)   # => false
```

条件に合うものの個数を調べる

```elixir
Enum.count([1, 2, 3, 4, 5], &rem(&1, 2) == 0)  # => 2
```

最大のものを返す

```elixir
Enum.max([1, 2, 3, 4, 5])                      # => 5
```

最小のものを返す

```elixir
Enum.min([1, 2, 3, 4, 5])                      # => 1
```

加工結果が最大のものを返す

```elixir
Enum.max_by([1, -2, 3, 4, -5], &abs/1)         # => -5
```

加工結果が最小のものを返す

```elixir
Enum.min_by([1, -2, 3, 4, -5], &abs/1)         # => -1
```

昇順にソートする

```elixir
Enum.sort([1, -2, 3, 4, -5])                   # => [-5, -2, 1, 3, 4]
```

加工結果で昇順にソートする

```elixir
Enum.sort_by([-5, -2, 1, 3, 4], &abs/1)    # => [1, -2, 3, 4, -5]
```

加工結果を配列で返す

```elixir
Enum.map([1, -2, 3, 4, -5], &abs/1)            # => [1, 2, 3, 4, 5]
```

繰り返しについて知りたければ [Enumerables and Streams](https://elixir-lang.org/getting-started/enumerables-and-streams.html) を読むとよいでしょう．

# サブルーチン

Elixir には「サブルーチン」というものはありません．Ruby のようにトップレベルでメソッド定義をすることもできませんが，モジュールとモジュール内関数を定義して，そのモジュールを `import` することで擬似的にトップレベルで関数を利用することができます．

```elixir
def hoge do
  "hoge"
end
# => ** (ArgumentError) cannot invoke def/2 outside module

defmodule Foo do
  def sum(x, y) do
    x + y
  end
end

import Foo
sum(1, 2)  # => 3
```

# ファイル入出力

ファイル入出力でできることは [File](https://hexdocs.pm/elixir/File.html) に書いてあります．

IO 関連について詳しく知りたければ [IO and the file system](https://elixir-lang.org/getting-started/io-and-the-file-system.html) を読むとよいでしょう．

## 読み込み

とりあえず `File.read/1` だけ覚えておけばなんとかなると思います．

```elixir
{:ok, str} = File.read("foo.txt") # => うまく読み込めた場合 str に foo.txt の内容が丸ごと文字列で代入される
```

## 書き込み

`File.write/2` だけ覚えておけばなんとかなると思います．

```elixir
File.write("foo.txt", "hogehoge") # => foo.txt に hogehoge と書き込む
```

# 知っておいた方がよい文法

## 真偽値

Ruby と同じで `false` と `nil` が偽，それ以外の全ての値は真です．

## コマンドライン引数や環境変数

[System](https://hexdocs.pm/elixir/System.html) から取得しましょう．

```
/Users/niku% echo 'defmodule My do; IO.inspect(System.argv); end' > system.exs
/Users/niku% elixir system.exs hoge fuga
["hoge", "fuga"]
```

## unless

あります．`if` の反対です．

全く同じものを Elixir で自作できます（！）

興味があれば [Macros](https://elixir-lang.org/getting-started/meta/macros.html) を眺めてみましょう．
ただし今ここが理解できなくても Elixir は普通に使えます．

## 後置IF

Ruby でいうところの

```ruby
puts "ok" if x == 1
```

という記法は使えません．（もしあったら教えてください）

if 式を一行で書きたいなら

```elixir
if x == 1, do: IO.puts "foo"
```

と書くことはできます．


## モジュール定義

`defmodule do ... end` で定義します．

Ruby に慣れた人は `defmodule X do` と `def x do` の 2 つの `do` を忘れがちになるので注意しましょう．

```elixir
defmodule Foo do
  def greeding(name) do
    "Hi! #{name}"
  end
end

Foo.greeding("niku") # => "Hi! niku
```

## その他よく使うクラス

Ruby でいうところのシンボルに相当するものとして atom というものがあります．記法も `:hoge` と同じです．

Ruby でいうところのブロック（Procオブジェクト）に相当するものとして無名関数があります．記法は

```elixir
add = fn a, b -> a + b end
add.(1, 2) # => 3
```

という感じです．

Ruby にはないのですが，Tuple という長さがあらかじめ決まっている配列のようなものを良く使います．記法は `{:ok, "hello"}` のように `{}` でくくります．

[Basic types](https://elixir-lang.org/getting-started/basic-types.html) に目を通しておくと大体の登場人物がわかるでしょう．

# 余談

## メソッドチェーン

Ruby などのオブジェクト指向だと結果がオブジェクトで返ってくるので，返ってきたオブジェクトにあるメソッドを `.` で呼び出すことで以下のようにメソッドチェーンできますよね．

```rb
[1, 2, 3, 4, 5].select { |e| e < 4 }.map { |e| e * 10 } # => [10, 20, 30]
```

Elixr だと結果がデータ型で返ってきて，データ型から関数を呼び出す方法はないのでメソッドチェーンできません．

```elixir
list = [1, 2, 3, 4, 5]
filtered = Enum.filter(list, fn e -> e < 4 end)
Enum.map(filtered, fn e -> e * 10 end) # => [10, 20, 30]
```

これだと書きづらいですよねえ．

さて，Elixir には「操作対象は必ず関数の第一引数になる」という掟があります．上の例で言うと `list` や `filtered` が操作されていますね．

つまり「関数の結果を受けて，次の関数の第一引数へ渡す」という記法があれば操作を繋げることができます．Elixir ではこれを `|>` ( [Kernel.|>/2](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2) ) という記法で実現しています．

これを使うと以下のように書くことができます．

```elixir
[1, 2, 3, 4, 5] |> Enum.filter(fn e -> e < 4 end) |> Enum.map(fn e -> e * 10 end) # => [10, 20, 30]
```

便利ですね．

さらに無名関数を `&` ( [Kernel.SpecialForms.&/1](https://hexdocs.pm/elixir/Kernel.SpecialForms.html#&/1) ) で省略して書く方法があるので慣れると以下のように圧縮して書くことができます．

```elixir
[1, 2, 3, 4, 5] |> Enum.filter(&(&1 < 4)) |> Enum.map(&(&1 * 10)) # => [10, 20, 30]
```

慣れるまで読むのが大変そうですけど，必要な部分だけ書けて便利ですね．

## Elixir をはじめてみたいなあという人へ

[プログラミングElixir](http://shop.ohmsha.co.jp/shopdetail/000000004675/) がとてもよくできています．日本語でもありますし，興味をもった人が最初に手にとる書籍として非常におすすめです．

公式ページにある [Getting Started](https://elixir-lang.org/getting-started/) もとてもよくできているので，プログラミングElixirを読みおわったあと，折をみて読みましょう．

この 2 つを読めば大体のことを始められ，わからないことも調べられるでしょう．わからないことは Elixir のコミュニティで聞いてみましょう．私が知っている，日本で Elixir を弄って遊んでいるコミュニティには

- [beam-lang.tokyo](https://beam-lang.connpass.com/)
- [fukuoka.ex](https://techjin.connpass.com/)
- [Sapporo.beam](http://sapporo-beam.github.io/)

があります．