Отправка сообщений в кругах. - PullRequest
2 голосов
/ 30 июля 2011

Я новичок в функциональном программировании и только что перешел с haskell (мне не очень понравилось) на erlang (мне это очень нравится).Поскольку я учусь как автодидакт, я наткнулся на эти упражнения и начал их выполнять.

Я дошел до этой проблемы:

  1. Напишите функцию, которая запускает 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.

Вот моя ссылка для рецензирования .

Ответы [ 4 ]

3 голосов
/ 30 июля 2011

Это упражнение стало обрядом для всех программистов на эрланге.Я дал ему рабочее решение здесь вместе с объяснением, которое может оказаться полезным.

1 голос
/ 18 октября 2014

Мой ответ.

-module(con_test).

start_ring(Msg, M, N) ->
    [First|_]=Processes=[spawn(?MODULE, ring, []) || _ <- lists:seq(1,N)],
    First ! {rotLeft(Processes), {Msg, M*N}}.

ring() ->
    receive
        {_List, {Msg, Count}} when Count == 0 ->
            io:format("~p got ~s. Enough! I'm out.~n", [self(), Msg]),
            exit(normal);
        {[Next|_] = List, {Msg, Count}} when Count > 0 ->
            io:format("~p got ~s. Passing it forward to ~p.~n", [self(), Msg, Next]),
            Next ! {rotLeft(List), {Msg, Count-1}},
            ring()
    after 1000 ->
        io:format("~p is out.~n", [self()]),
        exit(normal)
    end.

rotLeft([])     ->  [];
rotLeft([H|T])  ->  T ++[H].
1 голос
/ 30 июля 2011

Кто-то уже придумал ответ здесь -> http://simplehappy.iteye.com/?show_full=true

1 голос
/ 30 июля 2011

Сначала возродите их, а затем отправьте стартовый сигнал.

Сигнал запуска будет отправлен после того, как все процессы уже запущены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...