作为对现有答案的补充,我想展示一个更具相关性的解决方案,我希望它能够说明将声明性编程范式(例如逻辑编程)应用于这个问题的一些独特好处。
首先,让我们回顾一下任务:
打印从 1 到 100 的所有数字,但不是数字,而是打印...
- 'Fuzz' 如果数字是 3 的倍数
- '嗡嗡声'如果是 5 的倍数
- 和“FizzBuzz”(如果两者都有)。
我认为默认的假设是数字仅限于 整数。
为简单起见,让我们首先将自己限制为单个整数,然后描述该整数与所需输出之间的关系。
使用 Prolog 系统的CLP(FD) 约束进行声明性整数算术,上述三种情况可以直接转换为 Prolog :
integer_output(N, 'Fuzz') :- N #= 3*_。
integer_output(N, 'Buzz') :- N #= 5*_。
整数输出(N,'FizzBuzz'):- N #= 3*_,N #= 5*_。
但这还不是全部,因为这会产生例如:
?- integer_output(4, N)。
错误的。
因此,我们需要另外一种情况,例如,我们可以将其表述为:
整数输出(N,N):- N mod 3 #\= 0,N mod 5 #\= 0。
这只是说明如果其他情况都不适用,我们按原样输出数字。由此产生的关系非常普遍。例如,我们可以将它用于具体整数:
?- integer_output(1, O)。
O = 1。
?- integer_output(3, O)。
O = '绒毛' ;
错误的。
我们也可以用它来编写单元测试,例如:
?- integer_output(5, 'Buzz' )。
真的 。
在这里,预期的输出已经指定,我们可以使用相同的关系来询问输出是否符合要求。这是关系的一个非常好的属性,如果我们只在系统终端上编写输出而不是像上面那样将其显式地作为谓词参数,那么就不会那么容易了。
但还有更多!我们也可以在另一个方向使用相同的关系,例如:“哪些整数导致输出?” 这里是:Buzz
?- integer_output(I, 'Buzz')。
5*_680#=我。
这是对早期测试用例的大规模概括,可以作为我们已经涵盖所有用例的额外保证。事实上,我们甚至可以进一步概括这一点,得到最一般的查询,询问答案的一般情况:
?- integer_output(I, O)。
O ='模糊',
3*_742#=我;
O = '嗡嗡声',
5*_742#=我;
O = 'FizzBuzz',
5*_1014#=我,
3*_1038#=我。
让我们对输出进行更多推理。显然,我们期望每个可能的整数的输出都是唯一确定的,对吧?让我们通过询问该属性的反例来询问 Prolog是否如此:
?-差异(O1,O2),
整数输出(I,O1),
整数输出(I,O2)。
O1 =“绒毛”,
O2 = '嗡嗡声',
5*_1046#=我,
3*_1070#=我;
O1 =“绒毛”,
O2 = 'FizzBuzz',
5*_1318#=我,
3*_1342#=我,
3*_1366#=我。
现在看起来不太好:从上面看,我们已经怀疑可能存在相同整数 I
产生两个不同的、同样合理的输出O1
和 的情况O2
。
事实上,这是一个出现此问题的具体整数:
?-整数输出(15,O)。
O = '绒毛' ;
O = '嗡嗡声' ;
O = 'FizzBuzz' ;
错误的。
所以,事实证明,输出不是唯一确定的!让我们跟随我们的本能,马上问:
这是谁的错?
CLP(FD) 限制责任?
事实上,事实证明,使用声明式表述只是暴露了任务表述中的模糊性。过早地采用其中一种解决方案不会暴露这个问题。
可能的意思是一个任务描述,它在整数和输出之间引入以下关系:
integer_output(N, 'Fuzz') :- N #= 3*_, N mod 5 #\= 0。
integer_output(N, 'Buzz') :- N #= 5*_, N mod 3 #\= 0。
整数输出(N,'FizzBuzz'):- N #= 3*_,N #= 5*_。
整数输出(N,N):- N mod 3 #\= 0,N mod 5 #\= 0。
这产生:
?- integer_output(15, O)。
O = 'FizzBuzz' ;
错误的。
其他测试用例仍然按预期工作。
现在,使用此关系作为构建块,使用元谓词很容易将其提升到整数列表maplist/3
:
嘶嘶声(Ls):-
numlist(1, 100, Ls0),
地图列表(整数输出, Ls0,Ls)。
示例查询和回答:
?- fizz_buzz(Ls)。
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] ;
错误的。
请注意,我们自己并没有写任何东西:我们使用 Prolog 顶层来为我们写东西,并推理arguments。
优点很明显:我们可以再次为这样的谓词编写测试用例。例如,我们期望以下内容成立,并且确实如此:
?- Ls = [1,2|_] , fizz_buzz(Ls)。
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] 。
到目前为止,一切都是完全纯净的,可以在各个方向使用。我将根据您的需要格式化此类解决方案作为一个简单的练习。
如果您的 Prolog 系统不提供numlist/3
,您可以使用bagof/3
来获取从 1 到 100 的整数列表,如下所示:
?- bagof (L, (L in 1..100,indomain(L)), Ls)。
Ls = [1, 2, 3, 4, 5, 6, 7, 8, 9|...]。
因此,bagof/3
对于这项任务可能很有用,但我不建议将其用于副作用。