Last week I discovered a new Twitter account, the Perl Weekly Challenge. This is an initiative by Mohammad Anwar where Perl 5 and 6 programmer will get a programming challenge every week. There will be challenges both for beginners and advanced programmers.

This week we got the first two-part challenge. The first — the beginner challenge — was to substitute every ‘e’ in the string “Perl Weekly Challenge” and count every occurence of ‘e’. The second — the expert challenge — was to program a one-liner that solves the Fizz Buzz challenge for every integer from 1 through 20. But if the integer is divisible with 3 or 5 or both, the integer should be replaced with Fizz, Buzz or FizzBuzz respectively.

Let’s start with the latter. If we omit the one-liner requirement, an easy and relatively obvious solution would look something like these two:

// JavaScript/Node solution using if statements

for (i = 0; i < 20; i++) {

if (i % 3 == 0 && i % 5 == 0) console.log("Fizz Buzz");

else if (i % 3 == 0) console.log("Fizz");

else if (i % 5 == 0) console.log("Buzz");

else console.log(i);

} # A more Perl 6 variant with "switch/case" syntax

for 1..20 {

given $_ {

when $_ %% 3 && $_ %% 5 {

say "Fizz Buzz":

}

when $_ %% 3 {

say "Fizz";

}

when $_ %% 5 {

say "Buzz";

}

default {

say $_;

}

}

}

Now — one could always convert the Perl 6 code into a one-liner by removing the line feeds and adding ; after the }’s. But that would neither look nor be very elegant.

What I wanted was a solution that almost was — and almost read — like one single statement. What I came up with was this:

say gather { take "Fizz " if $_ %% 3; take "Buzz" if $_ %% 5 } || $_ for 1..20

If I may say so myself this code use gather in a rather clever way. Checking for fizz and buzz is no longer an either/or scenario, but an “and” scenario. gather/take will generate a one or two element list if the requirements are met; either (Fizz), (Buzz) or (Fizz Buzz) if both criteria are met. If none of the criteria are met, the || (“or”) selects and returns the integer instead.

The resulting output looks like this:

1

2

(Fizz )

4

(Buzz)

(Fizz)

7

8

(Fizz)

(Buzz)

11

(Fizz)

13

14

(Fizz Buzz)

16

17

(Fizz)

19

(Buzz)

Should you want to remove the parentheses from the output, add a join:

say join " ", gather { take "Fizz" if $_ %% 3; take "Buzz" if $_ %% 5; } || $_ for 1..20

But I wanted the code to be as short as possible — without join it is 9 characters shorter. The shorter version is the one I submitted.