I/O streams

Torbjorn Tornkvist tobbe@REDACTED
Tue Aug 27 08:57:55 CEST 2002


Talking about streams. Just for the fun of it, here's an old hack that
the great Phil Wadler helped me out with once.

Some examples:

%% Get the first 7 primes
1> S=ns:first(7,ns:primes()).
#Fun<ns.1.125034421>
2> ns:stream_to_list(S).
[2,3,5,7,11,13,17]

%% Example of appending streams
3> ns:stream_to_list(ns:first(8,ns:append(ns:first(5,ns:naturals()),
                                          ns:first(5,ns:naturals())))).
[0,1,2,3,4,0,1,2]

%% Random number generation
4> ns:stream_to_list(
     ns:first(10,
              ns:map(fun(X)-> round(X) end,
              ns:scale(100,ns:random_numbers(random:seed()))))).
[9,44,72,95,50,31,60,92,67,48]

%% Approx. of PI (the further you look into the stream the better approx.)
5> ns:stream_to_list(ns:first(10,ns:pi())).  
[2.44949,
 2.44949,
 2.44949,
 2.82843,
 2.73861,
 3.00000,
 3.24037,
 3.46410,
 3.28634,
 3.16228]

Cheers /Tobbe

-module(ns).
-author('tobbe@REDACTED').
%%-export([start/0]).
-compile(export_all).

-define( return(X) , fun() -> X end ).

-define( force(X) , begin XXXfunnyvar=X, XXXfunnyvar() end ).

-define( vforce(V,X) , begin V=X, V() end ).

-define( bind(X,D,K) , fun() -> X=?vforce(XXXfunnyvar1,D), ?vforce(XXXfunnyvar2,K) end ).

-define( cons(H,T) , ?return({cons,H,T}) ).

-define( nil(), ?return(nil) ).

%% A lazy first...

first(0,_) -> ?nil();
first(N,S) when N>0 ->
    ?bind(S1,S,
           case S1 of
	       nil ->
		   ?nil();
	       {cons,H,T} -> 
		   ?cons(H,first(N-1,T))
           end).

%% 24> statistics(reductions),ns:first(7,ns:primes()),statistics(reductions).   
%% {1705891,61}
%% 25> statistics(reductions),ns:stream_to_list(ns:first(7,ns:primes())),statistics(reductions).
%% {1708314,616}
%% 6> statistics(reductions),streams:first(7,streams:primes()),statistics(reductions).
%% {1712785,576}

nth(0,S) -> 
    case ?force(S) of
	{cons,H,T} -> H
    end;
nth(N,S) when N>0 -> 
    case ?force(S) of
	{cons,_,T} -> nth(N-1,T)
    end.

map(F,S) ->
    ?bind(S1,S,
	  case S1 of
	      nil ->
		  ?nil();
	      {cons,H,T} ->
		  ?cons(F(H),map(F,T))
	  end).

scale(C,S) ->
    map(fun(X) -> X*C end,S).

double(S) -> scale(2,S).

%% Example:
%%
%% 15> ns:stream_to_list(ns:first(8,ns:append(ns:first(5,ns:naturals()),ns:first(5,ns:naturals())))).
%% [0,1,2,3,4,0,1,2]

%% 77> statistics(reductions),ns:stream_to_list(ns:first(8,ns:append(ns:naturals(),ns:naturals()))),statistics(reductions).
%% {1909897,270}
%% 78> statistics(reductions),ns:lf(8,lists:append(lists:seq(1,500),lists:seq(1,500))),statistics(reductions). 
%% {1919732,1157}
    
append(S,R) ->
    ?bind(S1,S,
	    case S1 of
		nil -> R;
		{cons,H,T} -> ?cons(H,append(T,R))
	    end).

naturals() -> 
    integers_starting_from(0).

integers_starting_from(N) ->
    ?cons(N,integers_starting_from(N+1)).

primes() -> sieve(integers_starting_from(2)).

sieve(S) ->
    ?bind(S1,S,
	  (case S1 of
	      nil ->
		  ?nil();
	      {cons,H,T} ->
		  Pred = fun(X) -> 'not'(divisible(X,H)) end,
	          ?cons(H,sieve(filter(Pred,T)))
	  end)).

filter(Pred,S) ->
    ?bind(S1,S,
	  case S1 of
	      nil ->
		  ?nil();
	      {cons,H,T} ->
		  case Pred(H) of
		      true ->
			  ?cons(H,filter(Pred,T));
		      false ->
			  filter(Pred,T)
		  end
	 end).

divisible(X,Y) ->
    if
	X rem Y == 0 -> true;
	true -> false
    end.
    
'not'(true) -> false;
'not'(false) -> true.

gcd(A,0) -> A;
gcd(A,B) -> gcd(B,A rem B).

%% Try this:
%% 22> ns:stream_to_list(ns:first(10,ns:map(fun(X)-> round(X) end,ns:scale(100,ns:random_numbers(random:seed()))))).
%% [9,44,72,95,50,31,60,92,67,48]

random_numbers(Seed) ->
    ?cons(random:uniform(),random_numbers(Seed)).
    
cesaro_stream() ->
    F = fun(R1,R2) -> case gcd(R1,R2) of 1 -> true; _ -> false end end,
    map_successive_pairs(F,map(fun(X)-> round(X) end,scale(100,random_numbers(random:seed())))).

map_successive_pairs(F,S) ->
    ?bind(S1,S,
	  case S1 of
	      {cons,H,T} ->
		  case ?force(T) of
		      {cons,H2,T2} ->
			  ?cons(F(H,H2),
				map_successive_pairs(F,T2))
		  end
	  end).

monte_carlo(S,Nt,Nf) ->
    ?bind(S1,S,
	  (case S1 of
	      {cons,H,T} ->
		  Next = fun(Nnt,Nnf) -> 
				 Q = Nnt+Nnf,
				 ?cons(Nnt/Q,monte_carlo(T,Nnt,Nnf))
	                 end,
	          case H of
		      true  -> Next(Nt+1,Nf);
		      false -> Next(Nt,Nf+1)
		  end
	  end)).

%% An approximation of pi.
%% We will use the fact that the probability that
%% two integers choosen at random will have no factors
%% in common (i.e their GCD is 1) is 6/pi**2. The 
%% fraction of times the test is passed gives us an 
%% estimate of this probability. So the further you 
%% look into the stream, the better approximation you'll get.
pi() ->
    map(fun(P) -> math:sqrt(6/P) end,
	monte_carlo(cesaro_stream(),0,0)).




%% Converting routines: streams <--> lists

stream_to_list(S) ->
    case ?force(S) of
	{cons,H,T} ->
	    [H|stream_to_list(T)];
	nil ->
	    []
    end.

list_to_stream([H|T]) -> ?cons(H,list_to_stream(T));
list_to_stream([])    -> ?nil().




%% Misc. routines

rv(File,NewFile) when list(File) ->
    M = erl_kernel:file(File),
    {ok,T,_} = erl_kernel:module(M),
    {ok,F} = file:open(NewFile,write),
    erl_kernel:pp(T,F),
    file:close(F).


lf(0,_) -> [];
lf(N,[H|T]) -> [H|lf(N-1,T)].







More information about the erlang-questions mailing list