A bonus Erlang Thursday for everyone today.
This past weekend I came across the post Bro, Do You Even FizzBuzz?!? about solving FizzBuzz in Clojure without using the modulus operator.
After translating it to Ruby as a point for a co-worker and publishing that translation, I thought I would translate it to Erlang as well to see the difference.
-module(fizzbuzz). -export([fizzbuzz/1]). fizzbuzz(N) -> Results = do_fizzbuzz(N), lists:foreach(fun(X) -> io:format("~p~n", [X]) end, Results). do_fizzbuzz(N) -> Fizzes = cycle(["", "", "fizz"], N), Buzzes = cycle(["", "", "", "", "buzz"], N), FizzBuzzes = lists:zipwith(fun lists:append/2, Fizzes, Buzzes), Numbers = lists:seq(1, N), lists:zipwith(fun translate/2, Numbers, FizzBuzzes). cycle(List, N) -> lists:sublist(lists:append(lists:duplicate(N, List)), N). translate(Number, "") -> integer_to_list(Number); translate(_, Translation) -> Translation.
A couple of points to note in the Erlang solution.
First, Erlang doesn’t have any direct concept of lazy lists/sequences, and there is no cycle
function, so I had to improvise by calling lists:duplicate
, lists:append
, and lists:sublist
a list that cycles over the source list to create a list that has N
elements. While this is not exactly the most efficient way to do this, it shows that it can be done.
Second, the ability of using lists:zipwith
helps with some of the ideas of pipelining, as we can process the items as they are zipped together, instead of having to process them as a different step.
Third, instead of using a case statement, we can use a function with a guard clause to determine if a translation exists or not, and use the translation if so, and the number if not.
I hope this gives you some food for thought, and would love to hear your feedback on how this could be improved even more, or other ways that FizzBuzz can be done besides the usual pattern matching with the guard clauses checking the remainders.
–Proctor
I would propose a relatively simpler version:
-module(fb).
-export([fizzbuzz/1]).
fizzbuzz(N) ->
Values = [fb(I) || I io:format("~s ", [V]) end,
lists:foreach(Print, Values).
fb(I) when I rem 15 == 0 -> "fizzbuzz";
fb(I) when I rem 5 == 0 -> "buzz";
fb(I) when I rem 3 == 0 -> "fizz";
fb(I) -> integer_to_list(I).
Let me try again…
-module(fb).
-export([fizzbuzz/1]).
fizzbuzz(N) ->
Values = [fb(I) || I io:format(“~s “, [V]) end,
lists:foreach(Print, Values).
fb(I) when I rem 15 == 0 -> “fizzbuzz”;
fb(I) when I rem 5 == 0 -> “buzz”;
fb(I) when I rem 3 == 0 -> “fizz”;
fb(I) -> integer_to_list(I).
Ok, one last time with no HTML tags…
-module(fb).
-export([fizzbuzz/1]).
fizzbuzz(N) ->
Values = [fb(I) || I <- lists:seq(1, N)],
Print =
fun(V) -> io:format(“~s “, [V]) end,
lists:foreach(Print, Values).
fb(I) when I rem 15 == 0 -> “fizzbuzz”;
fb(I) when I rem 5 == 0 -> “buzz”;
fb(I) when I rem 3 == 0 -> “fizz”;
fb(I) -> integer_to_list(I).
Yeah, this is the way I normally write FizzBuzz. The version here was to see how that Clojure solution translated, and one way it might look like without using rem.
Was also a small experiment in seeing what a “cycle” function in Erlang might look like, up to a given length.