The Challenge:

Write one-liner to solve FizzBuzz problem and print number 1-20. However, any number divisible by 3 should be replaced by the word fizz and any divisible by 5 by the word buzz. Numbers divisible by both become fizz buzz.

It’s a common one, and my all-time favorite solution begins with:

import numpy as np
import tensorflow as tf

But there’s a word in there that changes everything.

“one-liner”

A one-liner is code that is typed in at the prompt, in the form perl -e '[CODE GOES HERE]'. It doesn’t have to be Perl – for example try this with Node: node -p '0.1 + 0.2' – but Perl borrowed a lot from sed and awk, which are meant to be used in this way.

This word brings me chills, because “one-liner” tends toward Perl Golf, and Perl Golf tends toward solutions that are oblique, indecypherable and unmaintainable.

Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it? – Brian Kernighan

So, I asked for clarification, and no, we’re wanting an elegant solution. Shub-Internet suggests elegant means (of a scientific theory or solution to a problem) pleasingly ingenious and simple, so let’s get simple!

#!/usr/bin/env perl
use strict;
use warnings;
for my $i ( 1 .. 20 ) {
    if ( $i % 3 == 0 && $i % 5 == 0 ) { print 'fizzbuzz' }
    elsif ( $i % 3 == 0 ) { print 'fizz' }
    elsif ( $i % 5 == 0 ) { print 'buzz' }
    else { print $i }
    print "\n";
}

Here we’re not holding onto flags or anything. Just one variable, $i. I defend the elegance of this code because there’s four cases: divisible by 3, divisible by 5, divisible by both, and divisible by neither.

This covers all four cases, but we can get simpler.

#!/usr/bin/env perl
use strict;
use warnings;
for my $i ( 1 .. 20 ) {
    if ( $i % 3 == 0 ) { print 'fizz' }
    if ( $i % 5 == 0 ) { print 'buzz' }
    if ( $i % 3 != 0 && $i % 5 != 0 ) { print $i }
    print "\n";
}

Quick Note: % is modulus, which isn’t a thing you’re taught until you start with computing. It’s basically remainders. Consider 10 / 3, which equals 3.3333333.... 10 % 3 is essentially 10 - (3*3), or 1. 9 - (3 * 3) is 0. If you don’t have a CS background, this makes up for a month of CS 101.

I don’t like the $i % $n == 0 test, and I think we can get better. I had tried ! $i % $n, but that didn’t behave for me, so I’m thinking ternary operators. For those who don’t know, we can take:

if ( $i % 3 ) { print '' }
else { print 'fizz' }

and replace it with

print $i % 3 ? '':'fizz';

This is not a Perlism; it exists in many languages.

Quick Note: Here, we are playing with truth. Within conditionals, false means 0 and true means not 0. With %3, we can get the values 0, 1, and 2, and both 1 and 2 count as true. That’s another free week of CS 101, and a little bit of Boolean for PHIL 131 - Introduction to Logic.

Using the ternary operator, we can get down to:

#!/usr/bin/env perl
use strict;
use warnings;
for my $i ( 1 .. 20 ) {
    print $i % 3 ? '':'fizz';
    print $i % 5 ? '':'buzz';
    print $i % 3 && $i % 5 ? $i : '';
    print "\n";
}

That’s … okay, but still not something I would type out.

So we drop the strictures. I will not make a program that does not start out with use strict; use warnings; but one-liners are different. Those can be ejected, and with it, my. And, also, $i;

perl -e 'for (1..20) { print ; print "\n" }'

This gives us that range, because unless we designate a variable, it’s $_. Read perlvar for more details.

A thing to remember is that we’re hitting on a point where both your shell and Perl have opinions about single-quotes and double-quotes. Perl allows you to replace 'foo' with q{foo}, and "bar" with qq{bar}, but we won’t be doing that here.

$ perl -e 'for (1..20) { print $_ % 3 ? "" : "fizz" ; print $_ % 5 ? "" : "buzz" ; print $_ % 3 && $_ % 5 ? $_ : "" ; print "\n" }'
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz

I have been informed that perl -E gives you all the features that have been added during the time of Modern Perl, which in this case, would allow me to swap print "\n" with say "", which I don’t regard as a big difference. Good to know in the future, because I love say, but in this case, macht nicht.

If I was more of a code golfer, I could probably make that more terse, but as is, I think this is elegant and readable, which is as good as a one-liner can be.

Now, what can I do with node -e?

If you have any questions or comments, I would be glad to hear it. Ask me on Twitter or make an issue on my blog repo.