プログラミング Elixir 確認メモ (2)

内包表記のやつ、ちょっとカンニングしたやつを読んでみます。コードが以下。

for a <- range, b <- range, a <= b, a*b <= n, do: a*b

これは素数ではない数の数え上げになっているはず。range が [1, 2, 3, 4, 5] とすると以下が戻った?

> n = 5
5
> range = MyList.span(1, 5)
[1, 2, 3, 4, 5]
> for a <- range, b <- range, a <= b, a*b <= n, do: a*b
[1, 2, 3, 4, 5, 4]

なんか勘違いしている風。と思ったら span(2, 5) だった。

> range = MyList.span(2, n)
[2, 3, 4, 5]
> (for a <- range, b <- range, a <= b, a*b <= n, do: a*b)
[4]

これだと 2*2 だけが残る。

OrganizingAProject-1

テキストおさらい。色々云々してると config あたりで躓くなど。確認したら 1.16 入れてるし。

テーブル整形のあたり

コード確認必要ですね。あと Excercise の回答が以下で公開とのこと。

実装が以下です。

defmodule Issues.TableFormatter do
  import Enum, only: [ each: 2, map: 2, map_join: 3, max: 1]

  def print_table_for_columns(rows, headers) do
    with data_by_columns = split_into_columns(rows, headers),
         columns_width   = widths_of(data_by_columns),
         format          = format_for(columns_width)
    do
      puts_one_line_in_columns(headers, format)
      IO.puts(separator(columns_width))
      puts_in_columns(data_by_columns, format)
    end
  end

  def split_into_columns(rows, headers) do
    for header <- headers do
      for row <- rows, do: printable(row[header])
    end
  end

  def printable(str) when is_binary(str), do: str
  def printable(str), do: to_string(str)

  def widths_of(columns) do
    for column <- columns, do: column |> map(&String.length/1) |> max
  end

  def format_for(column_widths) do
    map_join(column_widths, " | ", fn width -> "~-#{width}s" end) <> "~n"
  end

  def separator(column_widths) do
    map_join(column_widths, "-+-", fn width -> List.duplicate("-", width) end)
  end

  def puts_in_columns(data_by_columns, format) do
    data_by_columns
    |> List.zip
    |> map(&Tuple.to_list/1)
    |> each(&puts_one_line_in_columns(&1, format))
  end

  def puts_one_line_in_columns(fields, format) do
    :io.format(format, fields)
  end
end

テスト見れば大体わかりますが以下の理解が微妙。

    data_by_columns
    |> List.zip
    |> map(&Tuple.to_list/1)
    |> each(&puts_one_line_in_columns(&1, format))

data_by_columns は以下なリスト? として

[
    ["r1 c1", "r2 c1", "r3 c1", "r4 c1"],
    ["r1 c2", "r2 c2", "r3 c2", "r4++c2"],
    ["r1+++c4", "r2 c4", "r3 c4", "r4 c4"]
]

zip したらこうなるのか。

[
    {"r1 c1", "r1 c2", "r1+++c4"},
    {"r2 c1", "r2 c2", "r2 c4"},
    {"r3 c1", "r3 c2", "r3 c4"},
    {"r4 c1", "r4++c2", "r4 c4"}
]

確かに出力される順の並びになってますね。で次は要素な tuple を List に変換してるはず。最後に要素を出力、なのか成程。

複数プロセス

15.2 節の chain.exs については確認メモを控えます。

  • code_to_run は無名関数で Chain.counter に pid が渡される形

  • Enum.reduce(1..n, self(), code_to_run) は 1 から n まで数えあげしつつ code_to_run を呼び出す

    • 最初の第二引数は self() です

    • 以降は code_to_run が戻す pid が渡されます

    • 最終的に一番最後に spawn された pid が last に渡されます

  • で、コメントの通りで最後に作ったプロセスに 0 を渡して起動

  • 最後に起動されるはずの self() で定義されてる receive が最終結果を受け取る

テキストにあるように実行してみます。

# elixir -r chain.exs -e "Chain.run(10)"
{1973, "Result is 10"}
# elixir -r chain.exs -e "Chain.run(100)"
{2112, "Result is 100"}
# elixir -r chain.exs -e "Chain.run(1_000)"
{3514, "Result is 1000"}
# elixir -r chain.exs -e "Chain.run(10_000)"
{18652, "Result is 10000"}
# elixir -r chain.exs -e "Chain.run(400_000)"

01:50:51.625 [error] Too many processes


** (SystemLimitError) a system limit has been reached
    :erlang.spawn(Chain, :counter, [#PID<0.262246.0>])
    chain.exs:11: anonymous fn/2 in Chain.create_processes/1
    (elixir 1.16.1) lib/enum.ex:4391: Enum.reduce_range/5
    chain.exs:14: Chain.create_processes/1
    (stdlib 5.2) timer.erl:312: :timer.tc/4
    chain.exs:25: Chain.run/1
    nofile:1: (file)
# elixir --erl "+P 1000000" -r chain.exs -e "Chain.run(400_000)"
{788726, "Result is 400000"}
# elixir --erl "+P 1000000" -r chain.exs -e "Chain.run(1_000_000)"
{2699493, "Result is 1000000"}

これは強力。某所にて並列処理で大変だった記憶があるのですがこれは凄い。

WorkingWIthMultipleProcess-2

以下をでっち上げました。

defmodule WorkingWithMultipleProcess2 do
  def send_token do
    receive do
      {sender, msg} ->
        send sender, { :ok, "#{msg}" }
    end
  end
end

pid1 = spawn(WorkingWithMultipleProcess2, :send_token, [])
send pid1, {self(), "fred"}
pid2 = spawn(WorkingWithMultipleProcess2, :send_token, [])
send pid2, {self(), "betty"}

receive do
  {:ok, message} ->
    IO.puts message
end
receive do
  {:ok, message} ->
    IO.puts message
end

これだと必ずしも順番に出力されないことを確認。順序を決定的にするには

  • send_token で繰り返し

  • 同じプロセスに send
    という形で大丈夫みたいでした。

defmodule WorkingWithMultipleProcess2 do
  def send_token do
    receive do
      {sender, msg} ->
        send sender, { :ok, "#{msg}" }
        send_token()
    end

# 中略

pid1 = spawn(WorkingWithMultipleProcess2, :send_token, [])
send pid1, {self(), "fred"}
send pid1, {self(), "betty"}


この記事が気に入ったらサポートをしてみませんか?