Erlang Thursday Bonus – Functional fizzbuzz

今天给大家一个Erlang Thursday红包。

过去的这个周末我读了文章 Bro, Do You Even FizzBuzz?!? ,它阐述如何在Clojure里不用取模运算符来解决 FizzBuzz 问题。

在以同样的算法用Ruby来解决这个问题并发布博客后,我想我应该将这个问题用Erlang也来解决,同时也看看有何不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-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.

在Erlang的解决方案里有几个点要注意。

首先,Erlang没有任何懒列表或序列的直接概念,也没有 cycle 函数,所以我只能临时凑合着调用 lists:duplicate, lists:append 和 lists:sublist 来处理一个列表,从而循环处理原列表来生成一个有N个元素的列表。虽然这个做法不是最高效的方式,但是它是可行的。

其次,lists:zipwith 的能力对于一些管道的想法有帮助,因为我们可以处理这些链在一起的元素,而不是必须在不同的步骤中去处理它们。

第三,我们不用case语句,而是用一个有guard分支的函数来决定一个翻译存在与否,如果是是否用这个翻译,如果不是是否用这个数字。

我希望这起到抛砖引玉的作用,也希望听到你怎样更高效的方案,或者除了用一般的有guard分支来检查剩余元素的模式匹配的其他方法来解决FizzBuzz问题。

原文链接: https://www.proctor-it.com/erlang-thursday-bonus-functional-fizzbuzz/