読者です 読者をやめる 読者になる 読者になる

undefined

bokuweb.me

ElixirですごいE本 11章

Erlang Elixir

すごいErlangゆかいに学ぼう!

すごいErlangゆかいに学ぼう!

引き続き。

11.1 状態を述べろ

defmodule Kitchen do
  def fridge1 do
    receive do
      {{:store, food}, from} ->
        send from, {self(), :ok}
        fridge1
      {{:take, food}, from} ->
        send from, {self(), :not_found}
      :terminate -> :ok
    end
  end
end

実行する

ex(2)> c("ch11.ex")
ch11.ex:1: warning: redefining module Kitchen
ch11.ex:4: warning: variable food is unused
ch11.ex:7: warning: variable food is unused
ch11.ex:15: warning: variable food is unused
ch11.ex:18: warning: variable food is unused
[Kitchen]
iex(3)> fridge1 = spawn(Kitchen, :fridge1, [])
#PID<0.66.0>
iex(4)> send fridge1, {{:store, :apple}, self()}
{{:store, :apple}, #PID<0.57.0>}
iex(5)> flush
{#PID<0.66.0>, :ok}
:ok
iex(6)> send fridge1, {{:take, :apple}, self()}
{{:take, :apple}, #PID<0.57.0>}
iex(7)> flush
{#PID<0.66.0>, :not_found}
:ok
  • 食べ物を保存する場所がない
  • 状態を追加する必要がある
defmodule Kitchen do
  def fridge2(foodList) do
    receive do
      {{:store, food}, from} ->
        send from, {self(), :ok}
        fridge2 [food|foodList]
      {{:take, food}, from} ->
        case Enum.member? foodList, food do
          true ->
            send from, {self(), {:ok, food}}
            fridge2 List.delete foodList, food
          false ->
            send from, {self(), :not_found}
            fridge2 foodList
        end
      :terminate -> :ok
    end
  end
end
  • 状態を再帰で関数のパラメータに保持
iex(10)> c("ch11.ex")
ch11.ex:1: warning: redefining module Kitchen
ch11.ex:4: warning: variable food is unused
ch11.ex:7: warning: variable food is unused
[Kitchen]
iex(11)> pid = spawn(Kitchen, :fridge2, [[:baking_soda]])
#PID<0.80.0>
iex(12)> send pid, {{:store, :milk}, self()}
{{:store, :milk}, #PID<0.57.0>}
iex(13)> flush
{#PID<0.80.0>, :ok}
:ok
iex(14)> send pid, {{:store, :bacon}, self()}
{{:store, :bacon}, #PID<0.57.0>}
iex(15)> send pid, {{:take, :bacon}, self()}
{{:take, :bacon}, #PID<0.57.0>}
iex(16)> send pid, {{:take, :turkey}, self()}
{{:take, :turkey}, #PID<0.57.0>}
iex(17)> flush
{#PID<0.80.0>, :ok}
{#PID<0.80.0>, {:ok, :bacon}}
{#PID<0.80.0>, :not_found}
:ok

11.2 メッセージ大好きだけど秘密にしておいて

defmodule Kitchen do
  def store(pid, food) do
    send pid, {{:store, food}, self()}
    receive do
      {pid, msg} -> msg
    end
  end

  def take(pid, food) do
    send pid, {{:take, food}, self()}
    receive do
      {pid, msg} -> msg
    end
  end

  def fridge2(foodList) do
    receive do
      {{:store, food}, from} ->
        send from, {self(), :ok}
        fridge2 [food|foodList]
      {{:take, food}, from} ->
        case Enum.member? foodList, food do
          true ->
            send from, {self(), {:ok, food}}
            fridge2 List.delete foodList, food
          false ->
            send from, {self(), :not_found}
            fridge2 foodList
        end
      :terminate -> :ok
    end
  end
end
  • メッセージの抽象化を行う
iex(18)> c("ch11.ex")
ch11.ex:1: warning: redefining module Kitchen
ch11.ex:5: warning: variable pid is unused
ch11.ex:12: warning: variable pid is unused
ch11.ex:18: warning: variable food is unused
ch11.ex:21: warning: variable food is unused
[Kitchen]
iex(19)> pid = spawn(Kitchen, :fridge2, [[:baking_soda]])
#PID<0.94.0>
iex(20)> Kitchen.store pid, :water
:ok
iex(21)> Kitchen.take pid, :water
{:ok, :water}
iex(22)> Kitchen.take pid, :juice
:not_found
  • startを追加
defmodule Kitchen do
  def start(foodList) do
    IO.inspect __MODULE__ 
    spawn(__MODULE__ , :fridge2, [foodList])
  end
  
  ...
  
iex(1)> c("ch11.ex")
ch11.ex:1: warning: redefining module Kitchen
ch11.ex:10: warning: variable pid is unused
ch11.ex:17: warning: variable pid is unused
ch11.ex:23: warning: variable food is unused
ch11.ex:26: warning: variable food is unused
[Kitchen]
iex(2)> pid = Kitchen.start [:rhubarb, :dog, :hotdog]
Kitchen
#PID<0.65.0>
iex(3)> Kitchen.take pid, :dog
{:ok, :dog}
  • Elixirではmodule名は__MODULE__で取れる

11.3 タイムアウト

前回やってので省略。 afterでタイムアウト指定できる。

11.4 選択的受信

defmodule MultiProc do
  def important do
    receive do
      {priority, msg} when priority > 10 ->
        [msg | important()]
    after 0 ->
        normal()
    end
  end

  def normal do
    receive do
      {_, msg} ->
        [msg | normal()]
    after 0 ->
        []
    end
  end
end
iex(1)> c("ch11.ex")
ch11.ex:1: warning: redefining module Kitchen
ch11.ex:10: warning: variable pid is unused
ch11.ex:17: warning: variable pid is unused
ch11.ex:23: warning: variable food is unused
ch11.ex:26: warning: variable food is unused
[MultiProc, Kitchen]
iex(2)> send self(), {15, :high}
{15, :high}
iex(3)> MultiProc.important
[:high]
iex(4)> send self(), {15, :high}
{15, :high}
iex(5)> send self(), {17, :high}
{17, :high}
iex(6)> send self(), {9, :low}
{9, :low}
iex(7)> send self(), {5, :low}
{5, :low}
iex(8)> MultiProc.important
[:high, :high, :low, :low]

選択的受信の落とし穴

  • 無視したメッセージが多くなると読み込み時間が長くなる
    • プロセスサイズも大きくなる