トップ «前の日記(2008-07-06) 最新 次の日記(2008-07-08)» 月表示 編集

日々の流転


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を使うことで、指定した時間だけ待ったり、優先順位付きの受信をしたりといったことが出来るのが少し面白かった。 それから、プロセスのリンクやモニタのような機能は、他の言語の並行処理でも結構欲しくなることがあるので、これが組み込みであるのはいいなぁ。

Tags: Erlang

*1 残念ながら43行にはならなかったけど