2015-12-17 2 views
1

Я написал простую программу в Erlang. Его задача - вычислить значение заданного многочлена 2 * x^2 + 3 * x + 5. Оба 2 * x^2 и 3 * x вычисляются параллельно, и в самом конце эти обе части складываются вместе, 5 добавлен и, наконец, мы получаем результат. Существует одна функция, вычисляющая 2 * x^2, вторая - вычисление 3 * x и последняя, ​​которая вычисляет конечный результат. Вот код:Erlang - последовательные операторы получения с альтернативным сценарием

-module(count2). 
-compile([export_all]). 
%-export([f_main/0]). 

%computes the value of the polynomial 2*x^2 + 3*x + 5 

f_result() -> 
    {Arg1, Res_1} = receive 
     {f1, X1, Temp1} -> {X1, Temp1} 
    end, 
    Res_2 = receive 
     {f2, _, Temp2} -> Temp2 
    end, 
    Res = Res_1 + Res_2 + 5, %adds 5 to the result of 2*X^2+3*X 
    io:format("f(~p) = ~p~n",[Arg1, Res]), 
    f_result(). 

%when the line 14 was io:format("f(~p) = ~p~p,[X1, Res], the compilation error occured: 
%variable 'X1' unsafe in 'receive' 


f_2(PidWyn) -> %computes 2*X^2 
    receive 
     {f2, X} -> 
      Res = 2*math:pow(X,2), 
      PidWyn ! {f2, X, Res}, 
      f_2(PidWyn); 
     {finish} -> 
      io:format("f_2 : finish~n") 
end. 

f_1(PidWyn) -> %computes 3*X 
    receive 
     {f1, X} -> 
      Res = 3*X, 
      PidWyn ! {f1, X, Res}, 
      f_1(PidWyn); 
     {finish} -> 
      io:format("f_1 : finish~n") 
end.   

f_main() -> 
    PidW = spawn(?MODULE, f_result, []), %the process ID of the function that computes and displays the final result 
    Pid1 = spawn(?MODULE, f_1, [PidW]), 
    Pid2 = spawn(?MODULE, f_2, [PidW]), 
    L = [1,2,3,4,5], 
    [Pid1 ! {f1, X} || X <- L], %sends a series of messages to the function f_1 
    [Pid2 ! {f2, X} || X <- L], %sends a series of messages to the function f_2 
    Pid2 ! {finish}, 
    Pid1 ! {finish}, 
    PidW ! {finish}, %sends the message to the function f_result to make it stop 
    io:format("f_main : finish~n"). 

Как вы можете видеть, функция f_result имеет два receive заявления один за другим. Первый получает кортеж от f_1, а второй получает кортеж от f_2. После этого функция отображает результаты и вызывает сам вызов цикла.

Я хотел бы сделать f_result функция получить {finish} кортеж что бы сказать ему, чтобы перестать работать (аналогичное решение видна в f_1 и f_2 функции), но я не могу в месте, требуется receive должным образом. Я пытался поставить

{Arg1, Res_1} = receive 
       {f1, X1, Temp1} -> {X1, Temp1}; 
       {finish} -> io:format("f_result : finish~n"), 
        exit(0) 
       end, 

, но он делает следующий вывод:

f_main : finish 
f_result : finish 
f_1 : finish 
f_2 : finish 
ok 

я буду признателен, если вы можете дать мне несколько советов.

ответ

0

Хотя вывод, который вы видите, не обязательно означает, что с вашим кодом на самом деле что-то не так. Все сообщения в Erlang являются асинхронными и содержат только одно обещание. Два сообщения, отправленные из одного процесса в другой, поступят в том же порядке, если они когда-либо прибудут.

Во-первых, почему выход ничего вам не говорит. Операции ввода-вывода модуля io как io:format/1,2,3 выполняются с использованием сообщений. Это означает, что вы отправляете четыре сообщения из четырех процессов на серверный процесс io. (В вашем случае лидер группы, который по умолчанию назвал процесс user, выполняющий стандартные операции ввода-вывода.) Не гарантируется, что эти сообщения поступают в том же порядке, что и вы. Процессы включают четыре независимые пары процессов. Обычно они поступают в том же порядке, но на него нельзя положиться.

Во-вторых, у вас есть аналогичная проблема с вашим сообщением finish от f_main процесс до f_result. Сообщение могло и, вероятно, поступило бы в процессе f_result до результатов от f_1 и f_2. Это приведет к тому, что ваше первое получение в предлагаемом решении досрочно закончит ваш f_result.

Решение силы finish сообщение прибывает в надлежащем порядке с исходным кодом. Это можно сделать только в том случае, если он перемещается по тому же пути, с теми же парами процессов. Вы должны отправить finish на номер f_result через f_1. Таким образом, вы могли бы сделать это следующим образом:

-module(count2). 
-compile([export_all]). 
%-export([f_main/0]). 

%computes the value of the polynomial 2*x^2 + 3*x + 5 

f_result() -> 
    {Arg1, Res_1} = receive 
         {f1, X, Temp1} -> {X, Temp1}; 
         finish -> 
          io:format("f_result : finish~n"), 
          exit(normal) 
        end, 
    Res_2 = receive 
       {f2, _, Temp2} -> Temp2 
      end, 
    Res = Res_1 + Res_2 + 5, %adds 5 to the result of 2*X^2+3*X 
    io:format("f(~p) = ~p~n",[Arg1, Res]), 
    f_result(). 

f_2(PidWyn) -> %computes 2*X^2 
    receive 
     {f2, X} -> 
      Res = 2*X*X, 
      PidWyn ! {f2, X, Res}, 
      f_2(PidWyn); 
     finish -> 
      io:format("f_2 : finish~n") 
    end. 

f_1(PidWyn) -> %computes 3*X 
    receive 
     {f1, X} -> 
      Res = 3*X, 
      PidWyn ! {f1, X, Res}, 
      f_1(PidWyn); 
     finish -> 
      PidWyn ! finish, 
      io:format("f_1 : finish~n") 
    end.   

f_main() -> 
    PidW = spawn(?MODULE, f_result, []), %the process ID of the function that computes and displays the final result 
    Pid1 = spawn(?MODULE, f_1, [PidW]), 
    Pid2 = spawn(?MODULE, f_2, [PidW]), 
    L = [1,2,3,4,5], 
    [Pid1 ! {f1, X} || X <- L], %sends a series of messages to the function f_1 
    [Pid2 ! {f2, X} || X <- L], %sends a series of messages to the function f_2 
    Pid2 ! finish, 
    Pid1 ! finish, 
    io:format("f_main : finish~n"). 

И результат:

> count2:f_main(). 
f_main : finish 
f_1 : finish 
f_2 : finish 
f(1) = 10 
f(2) = 19 
ok 
f(3) = 32 
f(4) = 49 
f(5) = 70 
f_result : finish 

Заметьте, что только f(X) = Y и f_result : finish гарантированно появляются в таком порядке, потому что они являются сообщения из одного f_result процесса к user io сервер. (Даже f_main : finish и ok могут отображаться в обратном порядке из-за способа работы оболочки внутри, но нет необходимости знать эту деталь. Вероятно, вы никогда этого не увидите.)

+0

Большое спасибо за ваш изнурительный ответ. Собственно, после того, как я задал свой вопрос, я понял, что отправка атома 'finish' должна быть сопоставлена ​​с другими сообщениями, но ваш ответ позволил мне лучше понять этот вопрос. Я знаю, что этот пример довольно примитивен, но это просто академические упражнения, чтобы изучить основы Эрланг. – user3855877

1

В этом выпуске нет ничего плохого. В Erlang сообщения отправляются асинхронно, поэтому процесс, обрабатывающий f_result, обрабатывает сообщение раньше, чем процессы, выполняемые f_1 и f_2.

В качестве альтернативы, вместо exit(0), вы обычно пишете exit(normal), чтобы сигнализировать о том, что процесс завершается «нормально», а не из-за ошибки. Эта конвенция опирается на несколько мест. Например, процесс с выходом не normal приводит к тому, что связанные процессы также выходят, а с помощью стратегии супервизора transient вы можете заставить супервизор перезапустить процессы тогда и только тогда, когда их причина выхода не normal.

+0

Спасибо. BTW Я имел в виду значение 0 как «нормальное» значение, в то время как ненулевое значение указывает на некоторую ошибку (например, 'return 0;' statement в C/C++), но хорошо знать такое соглашение. – user3855877

Смежные вопросы