2008-07-07 [長年日記]
λ. サンタクロース問題
お約束ということで、サンタクロース問題に挑戦してみた*1。
-module(santa). -compile(export_all). santa([_,_,_,_,_,_,_,_,_]=Reindeers, Elves) -> work("delivering", 3000, Reindeers), santa([], Elves); santa(Reindeers, [_,_,_]=Elves) -> work("meeting", 1000, Elves), santa(Reindeers, []); santa(Reindeers, Elves) -> receive {reindeer, X} -> santa([X|Reindeers], Elves) after 0 -> receive {reindeer, X} -> santa([X|Reindeers], Elves); {elf, X} -> santa(Reindeers, [X|Elves]) end end. work(What, N, XS) -> io:format("Santa Claus starts ~s.~n", [What]), random_wait(N), io:format("Santa Claus ends ~s.~n", [What]), lists:foreach(fun (X) -> X!leave end, XS). srand() -> {X,Y,Z}=erlang:now(), random:seed(X,Y,Z). random_wait(N) -> receive after random:uniform(N) -> true end. loop(Santa, Kind, Name) -> random_wait(2000), io:format("~s comes.~n", [Name]), Santa!{Kind, self()}, receive leave -> random_wait(1000), io:format("~s leaves.~n", [Name]), loop(Santa, Kind, Name) end. start() -> Santa = spawn_link(fun () -> srand(), santa([], []) end), [ spawn_link(fun () -> srand(), loop(Santa, K, S ++ erlang:integer_to_list(X, 10)) end) || {S,K,N} <- [{"Elf", elf, 10}, {"Reindeer", reindeer, 9}] , X <- lists:seq(1,N) ].
タイムアウト節を持つreceiveを使うことで、指定した時間だけ待ったり、優先順位付きの受信をしたりといったことが出来るのが少し面白かった。 それから、プロセスのリンクやモニタのような機能は、他の言語の並行処理でも結構欲しくなることがあるので、これが組み込みであるのはいいなぁ。
*1 残念ながら43行にはならなかったけど