views:

265

answers:

5

Hi guys,

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.

A: 

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.

RichardC
The disco projekt is very interesting + I will buy the book . Thanks for your hints.
rick
+3  A: 

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.
Cayle Spandon
+1. Rick, notice the common pattern in iterate() where each step takes a list as input and returns the new list. This is how you avoid having to modify the list in place, as you'd do in an imperative language. You see this all the time in Erlang and other pure functional languages.
Warren Young
He explicitly asked for how to do a simulation using multiple processes, not just how to set up a loop. Though admittedly, he is really asking several questions in one.
RichardC
Is this [N0, N1, N2 | RemainingList] a possible syntax?How can processes be able to know their kneighbors dynamically?
rick
re> He explicitly asked for how to do a simulation using multiple processesYes, it would be interesting to implement a parallelized solution based on divide and conquer e.g. recursive list splitting (see http://vimeo.com/6624203 for inspiration).A solution with one process per element in the list seems problematic: the message overhead would swamp the advantage of parallelism and it would be difficult to synchronize the calculations.
Cayle Spandon
rick
+1  A: 

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.

rick
I updated my previous answer to also include a generalized answer for arbitrary list lengths.
Cayle Spandon
A: 

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.

ndim
A: 

Hi guys,

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, i.e. 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).
rick