[erlang-questions] first erlang program

Jérôme Desquilbet <>
Sat Jan 5 15:21:45 CET 2013


Hello Rusdom,

Le 04/01/2013 18:19, Rustom Mody a écrit :
> Wrote my first erlang program -- sieve of eratostenes;

Funny, it was also my first attempt to Erlang, starting from an Ada 
example in "Concurrent and Real-Time Programming in Ada" by Alan Burns 
and Andy Wellings; and we reprogrammed it (well, yet another variant) as 
a group exercise during our Erlang Dojo in Paris last December... :-)

Some comments below.

> --------------------------------------------
> -module(eratos).
> -export([main/0, gen/1, sieve/0, filtloop/2]).

Because you spawn the funs, you only need to export "main".

> -define (MAX, 200000).
> gen(Proc) -> gen(Proc,2).
> gen(Proc, N) ->
>      if N =< ?MAX -> Proc ! N,
>             gen(Proc, N+1);
>         true     ->  stop
>      end.
>
> filtloop(Prime, NS) -> % NS is NextSieve

Or rename "NS" into "Next_Sieve" :-)
And maybe refactor with two function clauses to avoid the if.

But the main issue I guess here is "stop", which should be more: send 
"stop" to the first sieve (prime number 2) and have it resend "stop" the 
next sieves so that they all stop. See also at the end of this email.
With your version, if you run the program from an erlang shell and then type

 > i().

you'll see that all the sieve-processes are still running.

>      receive
>      N when is_integer(N) ->

Well, you may think again about why you would really need the guard 
here. If it is just not to catch "stop", then move the "stop" clause 
below to the first place.

>          if N rem Prime =/= 0 ->
>              NS ! N;
>             true          -> ok
>          end,
>          filtloop(Prime,NS);
>      stop -> ok; % io:format("Stopping ~w~n", [Prime]);

Again, good idea but I don't think any "stop" message is ever sent.

>      XX -> io:format("Something strange in filtloop~w~n", [XX])

Indeed. But I think you have let go with defensive programming in Erlang.

>       end.
>
> sieve() ->
>      receive Prime -> ok end,
>      io:format("~w~n", [Prime]),
>      Nextsieve = spawn(fun sieve/0),
>      filtloop(Prime, Nextsieve).
>
> main() ->
>      gen(spawn (fun sieve/0)).

I think the Erlang way would be to name the "main" function "start" 
instead of "main". Juste because it is the default name from an erl 
command perspective: see "-run" at <http://www.erlang.org/doc/man/erl.html>.

> ------------------------------
>
>
> It is modelled after this shell script (consisting of 3 scripts)
>
> $ cat gen.sh
> i=2
> while true; do
>      echo $i
>      i=`expr $i + 1`
> done
> --------
> $ cat filt.sh
> while true; do
>    read x
>    if [ 0 != `expr $x % $1` ] ; then
>       echo $x
>    fi
> done
> -------------
> $ cat sieve.sh
> read x
> echo $x
> filt.sh $x | sieve.sh
> ----------------
> Call like this
> gen.sh | sieve
>
> Performance wise its fun to watch the erlang go 10 (50?) times faster
> than the shell script
> However the shell script has an elegance that the erlang does not have
> because I dont know how to 'anonymize'
>   the stdin/stdout that a classic Unix pipeline gives
>
> Also more basic noob questions like how to avoid the io:format
> statements etc

I think you have to put some concurrent observer pattern in place, in 
order to distribute the primes to whatever clients need them ?

> TIA for any tips/guidance
> Rusi
> --
> http://www.the-magus.in
> http://blog.languager.org

Here is how I refactored your code (still io:format()), but it's not 
optimal I think. I'll try to merge your version with ours, but later... :-)
Cordially,
   Jérôme.

--------8<-----------------

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

-define (MAX, 200000).

start() ->
	gen(spawn (fun sieve/0)).

gen(Sieve) -> gen(Sieve, 2).

gen(Sieve, N) when N =< ?MAX ->
	Sieve ! N,
	gen(Sieve, N+1);

gen(Sieve, N) ->
	io:format("Stopping at ~b~n", [N]),
	Sieve ! stop.

filtloop(Prime, NextSieve) ->
	receive
		stop ->
			NextSieve ! stop,
			exit(stopped);
		N ->
			if
				N rem Prime =/= 0 ->
					NextSieve ! N;
				true ->
					ok
			end,
			filtloop(Prime, NextSieve)
      end.

sieve() ->
	receive
		stop ->
			exit(stopped);
		Prime ->
			io:format("~b~n", [Prime]),
			NextSieve = spawn(fun sieve/0),
			filtloop(Prime, NextSieve)
	end.

--------8<-----------------




More information about the erlang-questions mailing list