Complementing the existing answers, I would like to show a more relational solution that I hope illustrates some unique benefits of applying a declarative programming paradigm such as logic programming to this question.
First, let us recapitulate the task:
print all numbers from 1 to 100, but instead of the number, print...
- 'Fuzz' if number is a multiple of 3
- 'Buzz' if multiple of 5
- and 'FizzBuzz' if both.
The tacit assumption, I presume, is that the numbers are limited to integers.
For simplicity, let us first restrict ourselves to a single integer, and let us describe the relation between such integer and the required output.
The three cases mentioned above can be quite directly translated to Prolog, using your Prolog ystem's CLP(FD) constraints for declarative integer arithmetic:
integer_output(N, 'Fuzz') :- N #= 3*_.
integer_output(N, 'Buzz') :- N #= 5*_.
integer_output(N, 'FizzBuzz') :- N #= 3*_, N #= 5*_.
That's not all though, because this yields for example:
?- integer_output(4, N).
false.
Hence, we need one more case, which we can for example formulate as:
integer_output(N, N) :- N mod 3 #\= 0, N mod 5 #\= 0.
This simply states that in the event that none of the other cases applies, we output the number as is. The resulting relation is very general. For example, we can use it for concrete integers:
?- integer_output(1, O).
O = 1.
?- integer_output(3, O).
O = 'Fuzz' ;
false.
And we can also use it to write unit tests, for example:
?- integer_output(5, 'Buzz').
true .
Here, the intended output is already specified, and we can use the same relation to ask whether the output will be as required. That's a quite nice property of relations, and would not be so easy if we only wrote the output on the system terminal instead of making it explicit as a predicate argument as we did above.
But there's even more! We can also use the same relation in the other direction, where we ask for example: "Which integers result in the output Buzz
?" Here it is:
?- integer_output(I, 'Buzz').
5*_680#=I.
That's a massive generalization of the earlier test case, and can serve as an additional assurance that we have covered all cases. In fact, we can even generalize this further, resulting in the most general query which asks how answers look like in general:
?- integer_output(I, O).
O = 'Fuzz',
3*_742#=I ;
O = 'Buzz',
5*_742#=I ;
O = 'FizzBuzz',
5*_1014#=I,
3*_1038#=I.
Let us reason more about the output. Obviously, we expect that the output is uniquely determined for each possible integer, right? Let us ask Prolog whether this is so, by asking for counterexamples of this property:
?- dif(O1, O2),
integer_output(I, O1),
integer_output(I, O2).
O1 = 'Fuzz',
O2 = 'Buzz',
5*_1046#=I,
3*_1070#=I ;
O1 = 'Fuzz',
O2 = 'FizzBuzz',
5*_1318#=I,
3*_1342#=I,
3*_1366#=I .
Now that doesn't look good : From the above, we already suspect that there may be cases of the same integer I
yielding two different, equally justifiable, outputs O1
and O2
.
And in fact, here's a concrete integer where this problem arises:
?- integer_output(15, O).
O = 'Fuzz' ;
O = 'Buzz' ;
O = 'FizzBuzz' ;
false.
So, it turns out, that the output is not uniquely determined! Let us follow our natural instinct and ask right away:
WHOSE FAULT IS THIS?
CLP(FD) CONSTRAINTS TO BLAME?
In fact, it turns out that using a declarative formulation has simply exposed an ambiguity in the task formulation. Prematurely committing to one of the solutions does not expose this problem.
What was probably meant was a task description that induces the following relation between an integer and the output:
integer_output(N, 'Fuzz') :- N #= 3*_, N mod 5 #\= 0.
integer_output(N, 'Buzz') :- N #= 5*_, N mod 3 #\= 0.
integer_output(N, 'FizzBuzz') :- N #= 3*_, N #= 5*_.
integer_output(N, N) :- N mod 3 #\= 0, N mod 5 #\= 0.
This yields:
?- integer_output(15, O).
O = 'FizzBuzz' ;
false.
The other test cases still work as expected.
Now, using this relation as a building block, it is easy to lift it to lists of integers, using the meta-predicate maplist/3
:
fizz_buzz(Ls) :-
numlist(1, 100, Ls0),
maplist(integer_output, Ls0, Ls).
Sample query and answer:
?- fizz_buzz(Ls).
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] ;
false.
Note that we are not writing anything ourselves: We are using the Prolog toplevel to do the writing for us, and reason about arguments.
The advantage is clear: We can again write test cases for such a predicate. For example, we expect the following to hold, and it does:
?- Ls = [1,2|_], fizz_buzz(Ls).
Ls = [1, 2, 'Fuzz', 4, 'Buzz', 'Fuzz', 7, 8, 'Fuzz'|...] .
So far, everything is completely pure and usable in all directions. I leave formatting such solutions as you want as an easy exercise.
If your Prolog system does not provide numlist/3
, you can use bagof/3
to obtain the list of integers from 1 to 100 like this:
?- bagof(L, (L in 1..100,indomain(L)), Ls).
Ls = [1, 2, 3, 4, 5, 6, 7, 8, 9|...].
Thus, bagof/3
can be useful for this task, but I cannot recommend to use it for side-effects.