Я новичок в функциональном программировании и только что перешел с haskell (мне не очень понравилось) на erlang (мне это очень нравится).Поскольку я учусь как автодидакт, я наткнулся на эти упражнения и начал их выполнять.
Я дошел до этой проблемы:
- Напишите функцию, которая запускает 2 процесса и отправляет сообщение M раз вперед и назад между ними.После того, как сообщения были отправлены, процессы должны завершиться изящно.
Я решил это следующим образом, и это работает (возможно, это можно сделать лучше; любой комментарий высоко ценится):
-module (concur).
-export ( [pingpong/1, pingpong/2] ).
pingpong (Msg, TTL) ->
A = spawn (concur, pingpong, ["Alice"] ),
B = spawn (concur, pingpong, ["Bob"] ),
B ! {A, TTL * 2, Msg}.
pingpong (Name) ->
receive
{From, 1, Msg} ->
io:format ("~s received ~p and dying.~n", [Name, Msg] ),
exit (From);
{From, TTL, Msg} ->
io:format ("~s received ~p.~n", [Name, Msg] ),
From ! {self (), TTL - 1, Msg},
pingpong (Name)
end.
Настоящая проблема заключается в следующем упражнении:
2) Напишите функцию, которая запускает N процессов в кольце и отправляет сообщение M раз вокруг всех процессов в кольце.После того, как сообщения были отправлены, процессы должны завершиться изящно.
Поскольку я отправляю сообщение не его отправителю, а следующему узлу в цепочке, я каким-то образом должен передать отправляющемуобрабатывать процесс получателя.Поэтому я предположил, что функция будет выглядеть примерно так:
pingCircle (Name, Next) ->
...
receive {TTL, Msg} -> Next ! {TTL - 1, Msg}
...
Но как мне начать все это.Когда я порождаю первую функцию в круге, я все еще не порождаю следующий узел и, следовательно, не могу передать его в качестве аргумента.Так что мой наивный подход не работает:
First = spawn (concur, pingCirle, ["Alice", Second] ),
Second = spawn (concur, pingCirle, ["Bob", Third] ),
...
Кроме того, подход рекурсивной передачи вызова spawn следующего узла в качестве параметра его предшественнику не решает проблему закрытия круга,т.е. передавая последний узел первому.
Вопрос: Как мне построить этот круг?
РЕДАКТИРОВАТЬ:
Благодаря вашим великолепным ответам я сумел достичь того, что хотел.Следовательно, этот вопрос решен.
Одно из возможных решений:
-module (concur).
-export ( [pingCircle/3, pingCircle/2] ).
pingCircle (Names, Message, TTL) ->
Processes = lists:map (fun (Name) -> spawn (?MODULE, pingCircle, [Name, nobody] ) end, Names),
ProcessPairs = lists:zip (Processes, rot1 (Processes) ),
lists:map (fun ( {Process, Recipient} ) -> Process ! {setRecipient, Recipient} end, ProcessPairs),
Circle = lists:map (fun ( {Process, _} ) -> Process end, ProcessPairs),
hd (Circle) ! {Message, TTL - 1, lists:last (Circle) }.
rot1 ( [] ) -> [];
rot1 ( [Head | Tail] ) -> Tail ++ [Head].
pingCircle (Name, Recipient) ->
receive
{setRecipient, NewRecipient} ->
pingCircle (Name, NewRecipient);
{Message, 0, Originator} ->
io:format ("~s received ~p with TTL 0 and dying.~n", [Name, Message] ),
if
Originator == self () -> io:format ("All dead.~n");
true -> Recipient ! {Message, 0, Originator}
end;
{Message, TTL, Originator} ->
io:format ("~s received ~p with TTL ~p.~n", [Name, Message, TTL] ),
if
Originator == self () -> Recipient ! {Message, TTL - 1, Originator};
true -> Recipient ! {Message, TTL, Originator}
end,
pingCircle (Name, Recipient)
end.
Вот моя ссылка для рецензирования .