Perl One Liner: FizzBuzz


Abstract

This document is the second one of a series in which I want to present and explain some of the Perl one liners that I wrote for fun or profit during the years. I hope that you will enjoy.

What's FizzBuzz?

I must confess that I ignore who invented the FizzBuzz problem but it is a nice, and simple, first interview question. It has both the intent of make the candidate relax and understanding if he/she is able to write a very simple program... and you cannot immagine how many people are not able to code this simple program.

The text of the exercise is more or less the following one: "Write a program that prints the numbers from 1 to 100. But for multiples of three print «Fizz» instead of the number and for the multiples of five print «Buzz». For numbers which are multiples of both three and five print «FizzBuzz»". It could be possible that I inverted Fiiz and Buzz... but the exercise is the same.

Our Implementation(s)

Anyway, we are not interested in providing a vanilla implementation for the FizzBuzz problem. We are looking for a Perl one-liner version. This means that the implementation will just consist of one or more simple instructions directly on the terminal command line using perl -e '...'.

Let's proceed with the first version (output is truncated):

nids@crimson: ~/Test/FizzBuzz$ perl -le 'for(1..100){$x=!($_%5);$y=!($_%3);$_=($y?"Fizz":"").($x?"Buzz":"")if($x+$y);print}'
1
2
Buzz
4
Fizz
Buzz
7
8
Buzz
Fizz
11
Buzz
13
14
FizzBuzz
16
.
.
.
.
.
97
98
Fizz
Buzz
nids@crimson: ~/Test/FizzBuzz$

It works. Let's explain why.

For each natural number in the range [1,100], $x will take the value true if the number is a multiple of 5 and $y will do the same if the number is a multiple of 3. This is done in the following way: if a number is divisible my another, the result of the modulo operation % is 0. 0 is equivalent to false, we negate it and we get true. More intesting are the other possible values of modulo: any possible other output of modulo is just another number and the logical negation of any possible number, different from 0, is 0.

We then use the if statement as a modifier. In Perl, the constuct STATEMENT if COND; is equivalent to if COND { STATEMENT; } and we just save some parenthesis.

We put the result of the construction of the output (if divisible by 3, 5 or both) in the automatic variable $_ that will be printed by default just calling print without any argument. If the number is not divisible by 3, 5 or both, we just leave it in $_, without touching it.

The condition of the if is an old C-programmer trick: booleans are just integers that can have value 0 or 1. We can then replace the || (or in Perl) with just one character: +.

Now, let's make it cooler.

Let's proceed with the second version (output is truncated):

nids@crimson: ~/Test/FizzBuzz$ perl -E 'say"Fizz"x!($_%5)."Buzz"x!($_%3)."$_"x(!!($_%5)&!!($_%3))for(1..100)'
1
2
Buzz
4
Fizz
Buzz
7
8
Buzz
Fizz
11
Buzz
13
14
FizzBuzz
16
.
.
.
.
.
97
98
Fizz
Buzz
nids@crimson: ~/Test/FizzBuzz$

First thing, we reverse the for statement and we make that postfix as we already did with the if in the previous case. The only problem with this is that the code executed as the body of the for must be a single statement.

First of all, we shorten the print using say instead. say acts exactly as print but it automatically adds a \n at the end of the printed string (say is available starting from Perl 5.10).

And now, the magic trick: we get rid of all the ifs.

Let's think about the logic of the application. There are 4 separate cases: if the number is a multiple of 3; it the number is a multiple of 5; if a number is both a multiple of 3 and 5; and all the other numbers.

The case in which a number is both a multiple of 3 and 5 is just a special case of the first two ones. From a certain point of view, we could write something like (pseudocode):

if number mod 5 == 0 then write "Fizz" endif
if number mod 3 == 0 then write "Buzz" endif
If the number is just a multiple of 5, the execution flow enters only the first statement but not the second. Similarly, a multiple of 3, we executes only the second one but for the numbers that are both multiples of 5 and 3 we execute both and we print "FizzBuzz".

How can we leverage this in Perl, in one statement without using ifs?

Perl has a special operator called x which works in the following way: given a string s on the left hand side of the operator and a number n on the right hand side, it repeats the string s n times concatenating that. For instance:

nids@crimson: ~/Test/Pick$ perl -E 'say "banana"x5'
bananabananabananabananabanana
nids@crimson: ~/Test/Pick$
As we already said, there are 4 separated cases that cannot happen at the same moment, or better, there are 3 cases, because the «both divisible by 3 and 5» can be reduced to the chaining of «divisible by 3» and «divisible 5».

what we do in the code above is exactly using the operator x, the chaining and the fact that booleans are just integers.

Since booleans are just integers, we can use them on the right hand side of the x operator to multiply the strings that we want to print by 0 or 1 only. If we repeat a string 0 times, we are just discarding that: the expression "Fizz"x!($_%5) does repeat the string "Fizz" only 0 or 1 times, depending on $_ being a multiple of 5 or not. The same applies to all the other cases and the output of each case is concatenated with the other in such a way that we can create the output string: the cases of 3 and 5 are not exclusive so we can write "Fizz", "Buzz" or "FizzBuzz" but they are both exclusive with the case in which a number is not a multiple of none of them and we just print it as it was inside the automatic variable $_.

All of this fits 67 characters. Can you do better than this? :¬)

Conclusions

In this document we saw a nice way to implement the FizzBuzz program and we tried to make it as short as possible. We were able to do that in only 67 characters, or bytes if you prefer.

But the must important thing is that we had a lot of fun!

Edit (2012-10-07 12.49 GMT)

One of the people visiting this page reported that there is a shorter version on Rosetta Code:

print 'Fizz'x!($_ % 3) . 'Buzz'x!($_ % 5) || $_, "\n" for 1 .. 100;

Stripping some spaces out of that and using say instead of print, we get to this:

nids@crimson: ~/Test/FizzBuzz$ perl -E 'say"Fizz"x!($_%3)."Buzz"x!($_%5)||$_ for 1..100'
1
2
Buzz
4
Fizz
Buzz
7
8
Buzz
Fizz
11
Buzz
13
14
FizzBuzz
16
.
.
.
.
.
97
98
Fizz
Buzz
nids@crimson: ~/Test/FizzBuzz$
Which is only 47 characters! Really impressive!

The solution uses the same idea of our solution but it actually uses a trick more: it uses the || or to decide if it need to print the crated string or the original value. This actually is very smart because in Perl the empty string is equivalent to the boolean value false.

Bravo!