唐揚げ弁当とプログラミング (Elixir編）

# はじめに

少し昔の記事になりますが、[こちらの記事](https://qiita.com/stkdev/items/5c021d4e5d54d56b927c)で「関数型プログラミング」について紹介されています。

そこではお題として、「唐揚げ弁当をつまみ食いする」の実装があげられていました。ここにそれを再掲させていただくと、

```text:
課題：
唐揚げ弁当がいくつかあるとします。それぞれ唐揚げが複数入っています。
この中からｘ個の唐揚げをつまみ食いするプログラムを作りましょう。
つまみ食いはバレないようにするために、
その時点で最も唐揚げ数が多いお弁当から取るという仕様にします。
```

となります。

そこで、関数型プログラミング言語である Elixir で作ってみました。

# コード

とりあえず、元記事では、オブジェクト指向プログラミングではBentooをクラスに、関数型プログラミングではデータタイプ的なJsonで取り扱っていました。そこで、Elixirでは何で扱うのが良いのか考えてみました。多分、普通に考えたら Elixirでも連想配列を使うのが良いのでしょう。
とはいえ、後々のプログラムの展開を考えると、Bentooクラスにはいろいろと新機能が追加されるかもしれません。だとすると、単純な連想配列とかではなく、応用の効くアクターモデルとして作った方が良いのではと考えて、次のように実装してみました。

```elixir
defmodule Bentoo do
  use GenServer

  def init({dish,num}) do
    {:ok, {dish,num}}
  end

  def handle_call(:show, _, {dish, num}) do
    IO.puts "#{dish}#{inspect num}個"
    {:reply, nil, {dish, num}}
  end

  def handle_call(:data, _, state) do
    {:reply, state, state}
  end
end
```

初期化を除けば、表示と値参照の2つしかメッセージハンドラのない簡単な作りです。Bentooクラスに相当します。

そして、Bentooモジュールの配列を作ります。

```elixir
    {:ok, pid1} = GenServer.start_link(Bentoo,{"唐揚げ",10})
    {:ok, pid2} = GenServer.start_link(Bentoo,{"唐揚げ",8})
    {:ok, pid3} = GenServer.start_link(Bentoo,{"唐揚げ",6})
    order = [pid1, pid2, pid3]
```

あとは、これを「つまみ食い」するたびに、変化させていけば良い訳なので、元記事の関数型プログラミングの方法のように、どれが一番唐揚げのたくさん入っている弁当かを調べるために

```elixir
  def selectMaxBentoo(order) do
    order |> Enum.reduce(nil,
      fn(x,acc) ->
        if acc == nil do x else
          {_, a} = GenServer.call(x, :data)
          {_, b} = GenServer.call(acc, :data)
          if (a > b) do x else acc end
        end
      end)
  end
```

と、実際につまみ食いをするために

```elixir
  def tsumamigui(order,pid) do
    order |> Enum.map(
      fn(x) ->
        if x == pid do
          {xdish,xnum} = GenServer.call(x,:data)
          GenServer.stop(x)
          {:ok, xpid} = GenServer.start_link(Bentoo,{xdish,xnum-1})
          xpid
        else
          x
        end
      end)
  end
```

を定義しました。

あとは、これをループで回せば良いので、

```elixir
    1..5 |> Enum.reduce(order,
      fn(_,order) ->
        pid = selectMaxBentoo(order)
        order2 = tsumamigui(order,pid)
        IO.puts "------"
        for item <- order2 do
          GenServer.call(item, :show)
        end
        order2
      end)
```
こんな感じです。

コードの全体は、

```elixir
defmodule Bentoo do
  use GenServer

  def init({dish,num}) do
    {:ok, {dish,num}}
  end

  def handle_call(:show, _, {dish, num}) do
    IO.puts "#{dish}#{inspect num}個"
    {:reply, nil, {dish, num}}
  end

  def handle_call(:data, _, state) do
    {:reply, state, state}
  end
end

defmodule Main do

  def selectMaxBentoo(order) do
    order |> Enum.reduce(nil,
      fn(x,acc) ->
        if acc == nil do x else
          {_, a} = GenServer.call(x, :data)
          {_, b} = GenServer.call(acc, :data)
          if (a > b) do x else acc end
        end
      end)
  end

  def tsumamigui(order,pid) do
    order |> Enum.map(
      fn(x) ->
        if x == pid do
          {xdish,xnum} = GenServer.call(x,:data)
          GenServer.stop(x)
          {:ok, xpid} = GenServer.start_link(Bentoo,{xdish,xnum-1})
          xpid
        else
          x
        end
      end)
  end

  def run do
    {:ok, pid1} = GenServer.start_link(Bentoo,{"唐揚げ",10})
    {:ok, pid2} = GenServer.start_link(Bentoo,{"唐揚げ",8})
    {:ok, pid3} = GenServer.start_link(Bentoo,{"唐揚げ",6})

    order = [pid1, pid2, pid3]

    1..5 |> Enum.reduce(order,
      fn(_,order) ->
        pid = selectMaxBentoo(order)
        order2 = tsumamigui(order,pid)
        IO.puts "------"
        for item <- order2 do
          GenServer.call(item, :show)
        end
        order2
      end)
  end

end

Main.run()
```

その結果は

```
------
唐揚げ9個
唐揚げ8個
唐揚げ6個
------
唐揚げ8個
唐揚げ8個
唐揚げ6個
------
唐揚げ7個
唐揚げ8個
唐揚げ6個
------
唐揚げ7個
唐揚げ7個
唐揚げ6個
------
唐揚げ6個
唐揚げ7個
唐揚げ6個
```
となりました。

# 感想

 2010年代に登場した、いわゆる次世代言語(SwiftやKotlinなども含む）はあまり触ったことがなかったので、名前だけは知っていたElixirで初めてプログラミングしてみました。アクターモデル(とErlang)というのは、私がオブジェクト指向プログラミングを最初に学んだ時期にも紹介されていて、オブジェクト指向プログラミングの王道であるカプセル化とメッセージパッシングを持っていたので、もしかしたら、現在の主流であるオブジェクト指向プログラミング（クラスベースの）の代わりに広まっていた可能性もあった興味深い言語だと思っています。
 現在のオブジェクト指向プログラミングでも、マルチスレッドのプログラミングは難しいですし、一方、並列処理や非同期処理の注目度が高まってきた昨今、Elixirへの期待も高まっていくかもしれないですね。

　また、比較のために、手続き指向型言語であるPascalで実装した[こちら](https://qiita.com/KNaito/items/35ccdda9b86ed33d73cd)も良かったら見て下さい。
Prologで実装した記事は[こちら](https://qiita.com/KNaito/items/2649a76ed014d49d9a93)です。