/var/log/messages

Jul 22, 2019 - 2 minute read - Comments - programming

Game of Life の実装 (3)

以下が確認済み。

  • Universe.Supervisor
  • Universe

続き、は Cell.Supervisor ですかね。

Cell の実装

  • 初期化コールバックにて Cell を子プロセスとして管理するよう登録
  • chidren と positions という API の定義

children の定義が以下です。

  # Return the PIDs of the children processes
  def children do
    Cell.Supervisor
    |> Supervisor.which_children
    |> Enum.map(fn {_, pid, _, _} -> pid end)
  end

コメントにある通り、pid なリストを戻している風なのですが

  • Cell.Supervisor が何を戻すのか
  • which_children という手続きについて

というあたりが微妙に謎。

ええと

which_children は Supervisor の pid を引数に取る、とのことで Cell.Supervisor は自身の pid を戻すのですかね。や、[{Id, Child, Type, Modules}] という記載がありますね成程。

あるいは positions

定義が以下です。

  # Return the positions {x: a, y: b} of the children processes
  def positions do
    children()
    |> Enum.map(&Cell.info/1)
    |> Enum.map(fn {x, y, n} -> %{x: x, y: y, n: n} end)
  end

Cell.info な定義は

  # Return info about a Cell (number of neighbours
  # and coordinates)
  def info(process) do
    GenServer.call(process, {:info})
  end

となっており、実体は以下。

  # Return info about the cell: {x, y, number_of_neighbours}
  def handle_call({:info}, _from, position) do
    {:reply, Tuple.append(position, do_count_neighbours(position)), position}
  end

  # Count the living neighbours of a Cell
  defp do_count_neighbours(position) do
    position
    |> neighbouring_positions
    |> keep_live
    |> length
  end

do_count_neighbours も面白そうです。パイプラインな手続きが以下なのですが

  # Compute the coordinates of the eight neighbouring
  # positions of a Cell at {x, y}
  defp neighbouring_positions({x, y}) do
    @offsets
    |> map(fn {dx, dy} -> {x + dx, y + dy} end)
  end

  # Determine which Cells to are alive
  defp keep_live(positions) do
    filter(positions, &(lookup(&1) != nil))
  end

@offsets は隣接セルとのオフセットな配列になってます。neighbouring_positions で位置を取得して keep_live でその Cell の生死を判断するために生きてる Cell をフィルタしてそのリストの length を戻しているのか成程。つうか lookup て何かというと以下でした。

  # Translate the Cell position {x, y} into the process
  # ID if the Cell is alive (avoid ghost neighbours)
  def lookup(position) do
    Cell.Registry
    |> Registry.lookup(position)
    |> Enum.map(fn
      {pid, _valid} -> pid
      nil -> nil
    end)
    |> Enum.filter(&Process.alive?/1)
    |> List.first
  end

これ、何となくそれぞれが戻すのがリストなので、という事に見えますね。単純に引数で渡された position が Cell として有効な状態なのであればその pid を戻す、という記述に見えます。

なので

Cell.Supervisor.positions については位置情報と有効な Cell の hash な配列が戻る、という理解で良いのかしら。

そろそろ

テスツ書きたくなってきました。OTP な試験について確認入れてみます。

Game of Life の実装 (2) Game of Life の実装 (4)

comments powered by Disqus