简体   繁体   中英

How can I write simulations in Erlang?

I want to do some numerical stuff in Erlang like this:

You've got an array with the following values:

[2,3,4]

In each iteration, you calculate

0.1 * [n-1] + 0.7 *[n] + 0.2 * [n+1]

This becomes the new [n] .

If n == 0 then [n-1] = 0. If [n] == length of array then [n] = 0.

So I try an example:

[2,3,4]

calculations:

0.1 * 0 + 0.7 * 2 + 0.2 * 3 = 2

0.1 * 2 + 0.7 * 3 + 0.2 * 4 = 3.1

0.1 * 3 + 0.7 * 4 + 0.2 * 0 = 3.1

So [2,3,4] becomes to [2, 3.1, 3.1] after one iteration.

I know how to write this in a non-functional language like C. But I have difficulties to imagine, how this could be done in Erlang. I found some tutorials on how you read a file to a list. So this is not the problem.

How can I spawn different Erlang processes that each process has one element of the list? How can I do calculations by communicating with 'the neighbors', so that the neighbors know where their neigbors are generically, without specifying each? How can I collect the data into a list?

Eventually, is it possible to solve that problem in Erlang at all?

Thanks for your help.

Answer for original question:

-module(simulation).
-export([start/0]).

f([N0, N1, N2]) ->
    M0 =            0.7 * N0 + 0.2 * N1,
    M1 = 0.1 * N0 + 0.7 * N1 + 0.2 * N2,
    M2 = 0.1 * N1 + 0.7 * N2,
    [M0, M1, M2].

iterate(List, Iterations) ->
    iterate(1, List, Iterations).

iterate(_Iteration, List, 0) ->
    List;
iterate(Iteration, List = [N0, N1, N2], MoreIterations) ->
    io:format("~.5f  ~.5f  ~.5f~n", [N0, N1, N2]),
    NextList = f(List),
    iterate(Iteration + 1, NextList, MoreIterations-1).

start() ->
    iterate([2.0, 3.0, 4.0], 10),
    ok.

Generalized answer for arbitrary list lengths:

-module(simulation2).
-export([start/0]).

f(Prev, Current, Next) ->
    0.1 * Prev + 0.7 * Current + 0.2 * Next.

transform(List) ->
    transform(0.0, List, 0.0).

transform(_First, [], _Last) ->
    [];
transform(First, [X], Last) ->
    Y = f(First, X, Last),
    [Y];
transform(First, [X1, X2 | More], Last) ->
    Y1 = f(First, X1, X2),
    [Y1 | transform(X1, [X2 | More], Last)].

iterate(List, Iterations) ->
    iterate(1, List, Iterations).

iterate(_Iteration, List, 0) ->
    List;
iterate(Iteration, List, MoreIterations) ->
    io:format("~p~n", [List]),
    NextList = transform(List),
    iterate(Iteration + 1, NextList, MoreIterations-1).

start() ->
    iterate([1.0, 2.0, 3.0, 4.0, 5.0], 10),
    ok.

That iterate pattern is great. But what do you do, if the list has more elements than three and every member needs his neighbors? This example would be more dynamic. Functional programming is a different way of thinking. But I think - as a novice - that it has the same potential as C. What shall I do, when I want to write:

start() ->
    iterate([2.0, 3.0, 4.0, 5.0, ...], 10),
    ok.

Thanks for all your hints. They are really helpful. I'm doing this simulation just for fun :). I worked on such a simulation in C. And I just wondered whether it would work dynamically in Erlang.

It's absolutely possible (but if it's pure numerical calculations you want to do, you might want to reconsider, unless it's the intellectual exercise you're after - or you could look at using a framework like http://discoproject.org/ ).

However, it seems that you haven't quite picked up the basic techniques of programming with processes in Erlang yet, so I suggest you start with that. (For example, get Joe Armstrong's book and play around with the examples.) Once you get into it - which doesn't take long - you shouldn't have much trouble figuring out some different ways of structuring such a program.

I would try the standard function pattern for processing a list

iter([], Acc) ->
    lists:reverse(Acc);
iter([H|Tail], Acc) ->
    iter(Tail, [do_something_with(H)|Acc]).

and extend it to use three values in its pattern as appropriate to deal with the three values your formula processes into the new value:

...
iter([H1,H2,H3|Tail], Acc) ->
    iter([H2,H3|Tail], [do_something_with(H1,H2,H3)|Acc]);
...

Of course, this needs a few extra function clauses to properly deal with the beginning and the end of the list.

I tried something, but failed. The following code should spawn one process per list element but somehow if I give it a list of 4 elements, it will not stop creating processes and crash then. The part where the bug - I think - occurs is marked with %BUG. simulation:simulate/2 starts everything, ie simulation:simulate([1,2,3,4],7), where 7 is the number of iterations. The element with the number 0 is created separately because it should never change it's value. The processes should get the PID of there previous and next neighbours so that they can exchange their values. I tried to debug it with the debuger im(). But it crashes. I saw that too many processes are created. Somehow, I don't find the mistake at the moment. Perhaps you notice something that's totally wrong?

-module(simulation).
-compile(export_all).
%-export().

f(Prev, Current, Next) ->
    0.1 * Prev + 0.7 * Current + 0.2 * Next.

simulate([], _Iteration) -> [];
simulate([X], 0) -> [X];
simulate([X], Iteration) -> simulate([f(0.0, X, 0.0)], Iteration - 1);
simulate(Liste, 0) -> Liste;
simulate(Liste, Iteration) ->
    PidStarter = self(),
    {Number, ProcList} = startProcesses(Liste, Iteration, PidStarter), %BUG
    Connector = spawn(fun() -> simProcessStarter(0.0, PidStarter, Iteration, 0) end), %untested
    [H1, H2 | _] = ProcList,
    H1 ! {startinformation, Connector, H2}, %untested
    ReversedProcList = lists:reverse(ProcList),
    [L1, L2 | _] = ReversedProcList,
    L1 ! {startinformation, L2, Connector},%untested
    fold(ProcList),%untested
    evaluate(Number, []).%untested

fold([]) -> ready;
fold([_X1]) -> ready;
fold([_X1, _X2]) -> ready;
fold([X1, X2, X3 | Tail]) ->
    X2 ! {statusinformation, X1, X3},
    fold([X2, X3 | Tail]).

evaluate(0, List) ->
    List;
evaluate(N, List) ->
    receive 
        {N, Current} -> 
            Result = [Current | List]
    end,
    evaluate(N-1, Result).

% returns {number of processes, list of processes started}
startProcesses(Liste, Iteration, PidStarter) -> startProcesses(Liste, 0, Iteration, [], PidStarter).

startProcesses([], N, _Iteration, List, _PidStarter) -> {N, lists:reverse(List)};
startProcesses([H | T], N, Iteration, List, PidStarter) ->
    startProcesses([T], N + 1, Iteration, [spawn(fun() -> simProcessStarter(H, PidStarter, Iteration, N + 1) end) | List], PidStarter).

simProcessStarter(Current, PidStarter, Iteration, Number) ->
    receive 
        {startinformation, PidPrev, PidNext} -> 
            Result = simProcess(Current, PidPrev, self(), PidNext, Iteration, Number)
    end,
    PidStarter ! Result.

simProcess(Current, _PidPrev, _PidCurrent, _PidNext, 0, Number) ->
    {Number, Current};
simProcess(Current, PidPrev, PidCurrent, PidNext, Iteration, Number) ->
    PidNext ! {prev, PidCurrent, Current, Iteration},
    receive
        {prev, PidPrev, Prev, Iteration} -> Prev
    end,
    PidPrev ! {next, PidCurrent, Current, Iteration},
    receive
        {next, PidNext, Next, Iteration} -> Next
    end,
    New = f(Prev, Current, Next),
    simProcess(New, PidPrev, PidCurrent, PidNext, Iteration-1, Number).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM