細かい話は置いといて、とりあえず触ってみたい人のための Elixir


## はじめに

この記事は、

fukuoka.ex Elixir／Phoenix Advent Calendar 2020 の 11日目です。

<https://qiita.com/advent-calendar/2020/fukuokaex>

前日は @tuchiro さん

Phoenix LiveViewへのサービスリプレイスの際に既存コンテンツからの段階的な移行を考えてみる でした。

<https://qiita.com/tuchiro/items/39ba2840e1cbb4567135>

### 対象

- プロゲート辺りを一通りやってみて、Ruby PHP JS 辺りを触ってみたよという段階の方
- プログラミングElixir読んで速攻挫折しかかっている方
- 中学生、高校生などで、環境構築について知見がないし、自分のPCもないよという方

### 環境

今回は環境構築も省きますので、paiza.io を使います。
プログラミングElixirではiexを使うので、環境構築が必要になりますが、そもそもそれも敷居が高いとか、ちょっと試してみたいだけ・・・という方もいらっしゃるはずです。
そういう方の為に朗報・・なんとpaiza.ioに Elixir Erlang があるんですよね。（スゴイ）

### 前提条件

文字列や数値、配列（リスト）連想配列（Hash）などのキーワードがある程度わかる方。

### 今回の目的

paizaのスキルチェックにElixirで挑戦出来るようになりたい。

## 1 まずは動かしてみましょう

### 1-1. paiza.ioを立ち上げてElixirを選択します

<https://paiza.io>　リンクはここです。 

[![Image from Gyazo](https://i.gyazo.com/e2834b6fe3018232785b0000c540ffe9.png)](https://gyazo.com/e2834b6fe3018232785b0000c540ffe9)


### 1-2. 最初のコードをかいてみましょう

こんなコードを書き込んでみて下さい。

```elixir

x = "hello"
IO.puts x
```

で、四角で囲んだ「実行」ボタンを押すと・・

[![Image from Gyazo](https://i.gyazo.com/31fe499b8b1c632bad44ea77b627dfd5.png)](https://gyazo.com/31fe499b8b1c632bad44ea77b627dfd5)

下線部分のように下窓に実行結果が出てきます。

さて、ここで `IO.puts` を使っていますが、これを使うのはここまでです。

以降は、 `IO.inspect` を使います。

早速書き換えてみましょう。

```elixir
x = "hello"
IO.inspect x
```

[![Image from Gyazo](https://i.gyazo.com/f527bd2874d5a585acf554f8896c38dc.png)](https://gyazo.com/f527bd2874d5a585acf554f8896c38dc)

`IO.puts` を使うと、 `hello` でしたが、 今回は、 `"hello"` となりました。

型が分かる、よりデバッグしやすい出力となります。

## 2. タプル

### 2-1. タプルって何？

タプルは、python 等を使われている方には馴染みかもしれませんが、そうでない方にはちょっと馴染みがない型となります。

`{}` で囲まれる型ですので、一瞬Hashとか連想配列的な何かと思いますが、Elixirのそれは、 `%{}` のように書きますので、別物となります。
また、キーバリューの形ではなく、配列（Elixirではリスト）の形にも似ています。

例えば、 

```elixir
{ "tokyo", "osaka", "fukuoka" }
```

や、

```elixir
{ :tokyo, :osaka, :fukuoka }
```


のように書かれたりします。(前者は要素が文字列型、後者は要素がアトム型（シンボルみたいなモノ）)

ちょっと試してみましょう。

```elixir
x = { "tokyo", "osaka", "fukuoka" }
IO.inspect x
```

このように書いてみましょう。

[![Image from Gyazo](https://i.gyazo.com/5df1777e317dcc07b101651dce782afb.png)](https://gyazo.com/5df1777e317dcc07b101651dce782afb)

実行結果に `{}` で囲まれた複数の要素が確認できます。

ちなみに、 `IO.puts x` を使うと

[![Image from Gyazo](https://i.gyazo.com/08288bc9185bf6960b71d1faf44fa6a3.png)](https://gyazo.com/08288bc9185bf6960b71d1faf44fa6a3)

エラーが出ますのでご注意を。

### 2-2. タプルが使われている場所

Rails辺りしか知らなくて、「いけるやろー」とPhoenix を使うと割と困るのですが、（経験談） タプルは、ファイルを読み込む時とか、ライブラリを使ってApiの結果を取得する時とか、何かしらの結果を表示した時に

```elixir
{ :ok, "何かしらの結果"}
{ :error, "何かしらの結果"}
```

みたいな感じで出力されるようです。

### 2-3. 値の取り出し方

まずは、通常考えられる取り出し方。

リスト同様キーがありませんので、数字を使います。

こんなコードを書いてみましょう。

```elixir
prefectures = { :tokyo, :osaka, :fukuoka }
IO.inspect elem(prefectures, 0)
IO.inspect elem(prefectures, 1)
IO.inspect elem(prefectures, 2)
```

[![Image from Gyazo](https://i.gyazo.com/59080fc051d4627d0c1b01a10cfbb2bf.png)](https://gyazo.com/59080fc051d4627d0c1b01a10cfbb2bf)

と、`elem/2` を使って書けます。

または、パターンマッチだと、

```elixir
{ x, y, z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x
IO.inspect y
IO.inspect z
```

こんな感じで取り出せます。

[![Image from Gyazo](https://i.gyazo.com/02dab1e58879bd9815077bbc98be1aeb.png)](https://gyazo.com/02dab1e58879bd9815077bbc98be1aeb)

ここでまた注意です。

```elixir
{ x, y, z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x
```
このように書くと、 `y` `z` が使われていないので、

[![Image from Gyazo](https://i.gyazo.com/b0831a0fae2b5fd88c7cdce37c491454.png)](https://gyazo.com/b0831a0fae2b5fd88c7cdce37c491454)

こんな、warning が出ます。そして、結果も出ません。

そういう場合は、

```elixir
{ x, _, _z } = { :tokyo, :osaka, :fukuoka }
IO.inspect x
```

`_` や `_z` のようにアンダースコアを書くことで、パターンマッチから無視されるようになります。

パターンマッチに関しては、長くなるのでここでは割愛させていただきます。

### 2-4. File.read

簡単な使用例を見てみましょう。

わかりやすいのはファイルを読み込んだ時です。

[![Image from Gyazo](https://i.gyazo.com/56d576213d66792d6148486479c844f5.png)](https://gyazo.com/56d576213d66792d6148486479c844f5)

paiza.io は別ファイルを作る事も出来ますので、ファイルを増やしてリネームします。

以下のファイルを作って下さい。

test.txt

```elixir
%{ :tokyo => "東京", :osaka => "大阪", :fukuoka => "福岡" }
```

[![Image from Gyazo](https://i.gyazo.com/28d987f7879c73d0fccbfcfbaf9c5e70.png)](https://gyazo.com/28d987f7879c73d0fccbfcfbaf9c5e70)

形としては、マップですが、取れるデーターは文字列となる想定です。（拡張子 exsでも同様の結果でした ）

そして、 Main.exs の方はこのように書きます。

Main.exs

```elixir
{ result, file } = File.read "test.txt"

IO.inspect(result)
IO.inspect(file)
```

実行結果は

[![Image from Gyazo](https://i.gyazo.com/325e3b9ac0cd95e031497bbed83d5b2c.png)](https://gyazo.com/325e3b9ac0cd95e031497bbed83d5b2c)

このようになります。

次に、ファイル名を存在しないものに変更します。

Main.exs

```elixir
{ result, file } = File.read "hogehoge.txt"

IO.inspect(result)
IO.inspect(file)
```

すると、結果は、

[![Image from Gyazo](https://i.gyazo.com/8df93b3fe3cb2a32b692654b49d951a7.png)](https://gyazo.com/8df93b3fe3cb2a32b692654b49d951a7)

この通り、最初の要素で、 `:ok` または、 `:error` という結論を返してきて、 成功ならデーターを、 なければ `:enoent` を返しています。

`enoent` はファイルやディレクトリがないよ〜という意味のようです。

では、取れた結果を元にちょっとした加工をやってみましょう。

### 2-5. パイプ演算子

想定として、

「マップのつもりでデーター見たら文字列だったでござる」

というつもりですので、ここからパイプ演算子を使って加工していきます。

パイプ演算子については、細かい話は割愛しますが、ざっくり・・メソッドチェーンのような処理が可能になる点と、元のデーターを汚染しない点について認識していただければと思います。

```elixir
{ result, file } = File.read "test.txt"

IO.inspect(result)

file
    |> String.replace(~r/"| |:|%{|}/, "")
    |> String.split(~r/,|=>/)
    |> Enum.chunk_every(2) 
    |> Enum.map(fn [k, v] -> {String.to_atom(k), v} end) 
    |> Map.new()
```

このような流れで考えてみます。

ただ、このまま実行すると経過が分からないので、

```elixir
{ result, file } = File.read "test.txt"

IO.inspect(result)

file
    |> String.replace(~r/"| |:|%{|}/, "")
    |> IO.inspect()

    |> String.split(~r/,|=>/)
    |> IO.inspect()
    
    |> Enum.chunk_every(2) 
    |> IO.inspect()
    
    |> Enum.map(fn [k, v] -> { String.to_atom(k), v } end) 
    |> IO.inspect()
    
    |> Map.new()
    |> IO.inspect()
    
IO.inspect(file)
```

このように inspect を挟んでいきましょう。

[![Image from Gyazo](https://i.gyazo.com/d011135c01c1228f66c27bb26543b14f.png)](https://gyazo.com/d011135c01c1228f66c27bb26543b14f)

 結果は見たとおり、 パイプで文字列を段階的に想定のデーターに加工ましたが、元の `file` に関しては最終行で出力した通り、無加工のものとなっています。
 
 ちなみに・・ですが、パイプ演算子を使って関数を実行している時、本来の第一引数は、省略されています。例えば、
 
 ```elixir
 file
   |> Map.new()
 ```
 
 だとしたら、
 
 ```elixir
 Map.new(file)
 ```
 
 がそもそもの引数の入り方という事です。
 
これを連続した処理をするために、第一引数を先頭から引き継いだ形としてパイプ演算子を使って省略出来るようになっています。（なんかカッコいい）

## 3. リストとEnum.map

### 3-1. ゴニョゴニョするならリスト

タプルは、処理が早いそのものに要素を足したり引いたり、加工することには向いていないようです。

ですので、データーの加工に関してはリストで行う方が良いようです。

ここで、せっかくpaizaを利用しているので、スキルチェックに出てくるような入力を受け取って、任意の結果が出せるように考えてみましょう。

### 3-2. Enum.map

問題： ある三角形の三辺の値が改行区切りで入力されてきます。

その三角形が、

- 正三角形の場合、「正三角形です」 
- 二等辺三角形の場合、「二等辺三角形です」
- その他の場合、「三角形です」

と出力しましょう。

的な出題を解こうとした時、

paiza.io には入力を取る機能もありますので、

[![Image from Gyazo](https://i.gyazo.com/c59d3922acad7a171c1f7056e1f93717.png)](https://gyazo.com/c59d3922acad7a171c1f7056e1f93717)

こんな感じで、

```
2
2
4
```

と二等辺三角形となる値をセットしてみましょう。

そして、コードはこのように書いてみます。

```elixir
data = [ nil, nil, nil ]
    |> Enum.map(fn s -> IO.gets(s) end)
    |> IO.inspect
    
    |> Enum.map(fn s -> String.trim(s) end)
    |> IO.inspect
    
    |> Enum.map(fn s -> String.to_integer(s) end)
    |> IO.inspect
    
    |> Enum.uniq
    |> IO.inspect
    
    |> Enum.count
    |> IO.inspect

result = fn
    1 -> "正三角形です"
    2 -> "二等辺三角形です"
    3 -> "三角形です"
end

IO.puts result.(data)
```

実行結果は、

[![Image from Gyazo](https://i.gyazo.com/b6a67d2e3c4948de1c93aa3864ab40c7.png)](https://gyazo.com/b6a67d2e3c4948de1c93aa3864ab40c7)

バッチリです。是非他の値も入れて遊んでみて下さい。

内容ですが、 `IO.gets/1` で入力値を待ちますが、これを 三回分実行しています。
後は、 `uniq`  で重複をまとめて要素の数をカウントすると、三角形の判断が出来ます。

パイプ演算子もカッコいいのですが、

```elixir
result = fn
    1 -> "正三角形です"
    2 -> "二等辺三角形です"
    3 -> "三角形です"
end
```

この、関数の中に `body` を複数持たせて `if` を書いていない所が素敵ですよね。

ELixirは恐ろしい程 ループや条件式の記述が書かれないので、非常に面白いと感じますね。

これでpaiza序盤を少し遊ぶ事が出来るのではないでしょうか？

是非腕試ししてみて下さい！

## 最後に

ここまでご覧頂きましてありがとうございます。

初学者に向けたものとして、当記事のEnum辺りの処理の元となった piacere さんの記事も是非！（私自身がすごく勉強させていただきました。）

こちらは続き記事で Phoenix も扱いますよ。

Excelから関数型言語マスター1回目：データ行の”並べ替え”と”絞り込み”

<https://qiita.com/piacerex/items/6714e1440e3f25fb46a1>

そして、明日は @koyo-miyamura さんの記事です。

<https://qiita.com/koyo-miyamura>