Agent を使って状態遷移を管理して解いてみた.
defmodule Shinchoku do
def start_link do
{:ok, pid} = Agent.start_link(fn -> "" end)
pid
end
# うまくいっている途中なら :progress
# 失敗してやりなおしなら :reset
# 終わったなら :done
# を返す
def get(pid, given) do
Agent.get_and_update(pid,
fn now ->
case {now, given} do
{"", "進捗"} -> {:progress, "進捗"}
{"進捗", "どう"} -> {:progress, "どう"}
{"どう", "です"} -> {:progress, "です"}
{"です", "か"} -> {:done, "か"}
_ -> {:reset, ""}
end
end)
end
def stream do
Stream.repeatedly(fn ->
Enum.shuffle(["進捗", "どう", "です", "か"]) |> hd
end)
|> Stream.transform({start_link, 0, :reset}, fn(str, {pid, n, state}) ->
if state === :done do
{:halt, {pid, n, state}}
else
amount = n + String.length(str)
state = get(pid, str)
{[{str, amount, state}], {pid, amount, state}}
end
end)
end
end
:random.seed(:os.timestamp)
Shinchoku.stream
|> Stream.each(fn {str, _amount, _state} -> IO.write(str) end)
|> Stream.each(fn {_str, amount, state} ->
if state === :done, do: IO.write("???\n#{amount}文字で煽られました.!!!!!\n")
end)
|> Stream.run
#> 進捗どうどう進捗ですですかどうですですかかかかどうか進捗かどう進捗ですか進捗進捗かどうですです進捗ですかですですどうかかかどうどうですどう進捗どうですどう進捗ですかですですかどう進捗ですですどうかですどうですかかどうかどうかどう進捗どうかどうかですです進捗進捗です進捗進捗どうどうです進捗です進捗か進捗どうどうどう進捗かどうですどうどうかどうですどうかかどうです進捗どうどう進捗進捗かかですですどう進捗かかかです進捗か進捗進捗ですどうですどうどうどうですかですですかどうどうどうです進捗どうですか???
#> 248文字で煽られました.!!!!!
Stream.transform/2 の第二引数に渡す関数の返り値が {:halt, acc}
だと Stream は終わるので,明示的に終了を試みなくても勝手に終わる.
Stream の内容は以下のデバッグのように
{文字, これまでの文字数, 状態}
な 3 要素のタプルになっている.
状態が :done
になったら終わりを意味する.
Shinchoku.stream
|> Stream.each(&IO.inspect/1)
|> Enum.take(10)
#> {"どう", 2, :reset}
#> {"どう", 4, :reset}
#> {"進捗", 6, :progress}
#> {"どう", 8, :progress}
#> {"か", 9, :reset}
#> {"進捗", 11, :progress}
#> {"か", 12, :reset}
#> {"です", 14, :reset}
#> {"か", 15, :reset}
#> {"どう", 17, :reset}