-module(demo_counting). % Erlang examples of "how to count". % % For experienced programmers in "normal" (non-functional) languages % who are wondering how one can program without looping constructs % ("for", "do/while") % % Copyright (c) 2008 Matt Kangas % % $Id$ -export([test/0]). test() -> io:fwrite("How many ways can we count to 10?~n"), io:fwrite("-- 1 --~n"), counter1(), io:fwrite("~n-- 2 --~n"), counter2(), io:fwrite("~n-- 3 --~n"), counter3(), io:fwrite("~n-- 4 --~n"), counter4(), io:fwrite("~n-- 5 --~n"), counter5(), io:fwrite("~n-- 6 --~n"), counter6(), io:fwrite("~n-- 7 --~n"), counter7(), io:fwrite("~n-- 8 (backwards) --~n"), counter8(), io:fwrite("~n-- 9 --~n"), counter9(), io:fwrite("~n-- 10 --~n"), counter10(), io:fwrite("~n-- matrix1 --~n"), count_matrix1(), io:fwrite("~n-- matrix2 --~n"), count_matrix2(), io:fwrite("~n-- matrix3 --~n"), count_matrix3(), io:fwrite("~n-- matrix4 --~n"), count_matrix4(), io:fwrite("~n-- matrix5 --~n"), count_matrix5(), io:fwrite("~n-- matrix6 --~n"), count_matrix6(), io:fwrite("~n-- matrix7 --~n"), count_matrix7(), io:fwrite("~nHappy happy joy joy!~n"). % ---------------------------------------------- % Linear examples % ---------------------------------------------- %% Example 1: % Build a list, a print function, apply the function to the list % lists:map() would work too. (It returns a value, lists:foreach() does not) counter1() -> L = lists:seq(1,10), PrintInt = fun(I) -> io:fwrite("~p ", [I]) end, lists:foreach(PrintInt, L). % Helper for below. "io:format()" and "io:fwrite()" are the same function printInt(I) -> io:format("~p ", [I]). % Example 2: % Same as counter1, but use a function reference instead of an inline lambda counter2() -> L = lists:seq(1,10), lists:foreach(fun printInt/1, L). %% Example 3: % Same as counter2, but use a list comprehension instead of lists:foreach/2 counter3() -> L = lists:seq(1,10), [ printInt(I) || I <- L]. %% Example 4: Recursion with an accumulator. % Use a guard sequence ("when N > 10") to specify our base case (termination criteria). % Very efficient, since it doesn't allocate a list of numbers, and is tail-recursive % % We define two functions, "counter4/0" (zero args) and "counter4/1". % "counter4/1" consists of two clauses separated by ";" counter4() -> counter4(1). counter4(N) when N > 10 -> none; counter4(N) -> printInt(N), counter4(N + 1). %% Example 5: Recursive list processing. % Nibble one item off the head of the list on each pass. % [H|T] works like car/cdr in lisp counter5() -> counter5( lists:seq(1,10) ). counter5([]) -> none; counter5([H|T]) -> printInt(H), counter5(T). %% Example 6: List processing with an accumulator counter6() -> %L = [1,1,1,1,1,1,1,1,1,1], L = lists:duplicate(10,1), counter6(L, 0). counter6([], Acc) -> Acc; counter6([H|T], Acc) -> N = H + Acc, printInt(N), counter6(T, N). %% Example 7: Use lists:foldl(). Much cleaner than above! counter7() -> L = [1,1,1,1,1,1,1,1,1,1], F = fun(A, AccIn) -> K = A + AccIn, printInt(K), K end, lists:foldl(F, 0, L). %% Example 8: lists:foldr() – not tail-recursive, % therefore not to be used when foldl() will work instead. % Note that the list comes out backwards :-) counter8() -> L = lists:seq(1,10), F = fun(A, AccIn) -> printInt(A), AccIn end, lists:foldr(F, void, L). %% Example 9: Anonymous recursive function, per % http://www.trapexit.org/forum/viewtopic.php?p=25927 % Disadvantages: % - Need to pass the function as an argument to itself % - Self recursive anonymous functions don't handle hot code updates counter9() -> F = fun(_,N) when N > 10 -> none; (G,N) -> printInt(N), G(G,N+1) end, F(F,1). %% Example 10: % Use Y combinator to "simplify" anonymous recursion. Crazy? Yes! :-) % But what the hell... % Recipe from http://bc.tech.coop/blog/070611.html y(M) -> G = fun (F) -> M(fun(A) -> (F(F))(A) end) end, G(G). counter10() -> X = fun (F) -> fun (N) when N > 10 -> none; (N) -> printInt(N), F(N+1) end end, (y(X))(1). % ---------------------------------------------- % Matrix examples % ---------------------------------------------- %% Matrix Example 1: Recursive matrix processing. % 5x5 matrix, zero-relative BUT it counts TOO FAR along the Y-axis % -- count_matrix1() -> count_matrix1( 5, 0, 0 ). count_matrix1(Size, Size, Size) -> none; count_matrix1(Size, Size, PtrY) -> io:format("~n"), count_matrix1(Size, 0, PtrY+1); count_matrix1(Size, PtrX, PtrY) -> io:format("(~p,~p) ", [PtrX, PtrY]), count_matrix1(Size, PtrX+1, PtrY). %% Matrix Example 2: Recursive matrix processing. % 5x5 matrix, use guard clauses to fix problems above % -- count_matrix2() -> count_matrix2( 5, 1, 1 ). count_matrix2(Size, _, PtrY) when (PtrY =:= Size+1) -> none; count_matrix2(Size, PtrX, PtrY) when (PtrX =:= Size+1) -> io:format("~n"), count_matrix2(Size, 1, PtrY+1); count_matrix2(Size, PtrX, PtrY) -> io:format("(~p,~p) ", [PtrX, PtrY]), count_matrix2(Size, PtrX+1, PtrY). %% Matrix Example 3: % Counts negative->positive values, otherwise similar as above. % Wouldn't it be nice to abstract the print logic buried inside this loop? % -- count_matrix3() -> count_matrix3( -3, 3 ). count_matrix3(Min, Max) -> count_matrix3( Min, Max, Min, Min ). count_matrix3(_Min, Max, _X, Y) when (Y =:= Max+1) -> none; count_matrix3(Min, Max, X, Y) when (X =:= Max+1) -> io:format("~n"), count_matrix3(Min, Max, Min, Y+1); count_matrix3(Min, Max, X, Y) -> io:format("(~p,~p) ", [X, Y]), count_matrix3(Min, Max, X+1, Y). %% Matrix Example 4: % Abstracts Min, Max, *AND* the print logic now! :-) % This one handles non-symmetric min/max values, as we demonstrate % % Still feels a bit clunky. I don't like the "Max+1" to indicate EOL, % -- count_matrix4() -> Min = -1, Max = 5, F = fun (X,_Y) when X =:= Max+1 -> io:format("~n"); (X,Y) -> io:format("(~p,~p) ", [X, Y]) end, count_matrix4(F, Min, Max). count_matrix4(F, Min, Max) -> count_matrix4(F, Min, Max, Min, Min ). count_matrix4(_F, _Min, Max, _X, Y) when (Y =:= Max+1) -> none; count_matrix4(F, Min, Max, X, Y) when (X =:= Max+1) -> F(X,Y), count_matrix4(F, Min, Max, Min, Y+1); count_matrix4(F, Min, Max, X, Y) -> F(X,Y), count_matrix4(F, Min, Max, X+1, Y). % ========================================================= % The following examples were generously provided by % Philip Robinson via email, Jan 31 2008 % % http://chlorophil.blogspot.com/ % clorophil-AT-gmail-DOT-com % % Thanks Philip! % ========================================================= % Iterative, nested: count_matrix5() -> L = lists:seq(-1, 5), lists:foreach( fun(Y) -> lists:foreach(fun(X) -> io:format("(~p,~p) ", [X, Y]) end, L), io:format("~n") end, L). % Nested list comprehensions. % (Inefficiently builds and discards the map result): count_matrix6() -> L = lists:seq(-1, 5), [(fun(J) -> [(fun(I) -> io:format("(~p,~p) ", [I,J]) end)(X) || X <- L], io:format("~n") end)(Y) || Y <- L], ok. % Generically traverse an N-dimensional matrix. % Axis ranges are given in reverse order (Z, Y, X...): count_matrix7() -> count_matrix7([{-1,5},{-1,5}]), % 2D matrix as above count_matrix7([{0,2},{0,3},{0,4}]). % 3D matrix count_matrix7(Ranges) -> count_matrix7(Ranges, []). count_matrix7([{Min,Max}|Ranges], Curr) -> lists:foreach(fun(C) -> count_matrix7(Ranges, [C|Curr]) end, lists:seq(Min, Max)), io:format("~n"); count_matrix7([], Curr) -> count_matrix7_print(Curr). count_matrix7_print([N]) -> io:format("(~p) ", [N]); count_matrix7_print([N|Rest]) -> io:format("(~p", [N]), count_matrix7_print2(Rest). count_matrix7_print2([N|Rest]) -> io:format(",~p", [N]), count_matrix7_print2(Rest); count_matrix7_print2([]) -> io:format(") ").