A recent discussion on interviewing programmers (in hopes of finding clueful ones) brought up the FizzBuzz challenge. Can you write a program to print the numbers from one to one hundred, printing also “Fizz” for multiples of three, “Buzz” for multiples of five, and “FizzBuzz” for multiples of three and five?
This ought to take no more than a few minutes for a developer with any proficiency in a language. I decided it would be fun to write it in Parrot’s PIR. There’s the straightforward procedural way, the array overloading way, an object-oriented way, the coroutine approach, and the generator technique.
I chose the first two, but I also decided to work entirely with test-driven development, even though this is normally the realm of a SpikeSolution–I thought that might be more interesting for everyone.
To make TDD work well, I created a test file,
t/examples/fizzbuzz.t. It’s a pure-PIR test script that uses the
Parrot version of Test::More and produces Test::Harness
compatible TAP.
The program starts out simply:
1 #!parrot
2
3 .include 'hllmacros.pir'
4
5 .const string TESTS = 18
The .include directive works like #include in
C; it inserts the contents of another source file into the current
compilation unit. hllmacros.pir contains some useful macros. PIR is
powerful, but it’s still an assembly language at heart. We’ve developed a
few shortcuts for making PIR easier to write.
Line 5 declares a constant–the number of tests to run. PIR has typed variables (actually four distinct types of registers), so all named variable declarations need a type declaration.
7 .sub 'main' :main
8 load_bytecode 'Test/More.pir'
9 load_bytecode 'examples/pir/fizzbuzz.pir'
10
11 .local pmc import_sub
12 .IMPORT( 'Test::More', 'plan', import_sub )
13 .IMPORT( 'Test::More', 'is', import_sub )
14 .IMPORT( 'Test::More', 'diag', import_sub )
15
16 plan( TESTS )
17
18 test_function( 'procedural' )
19 test_function( 'keyed_array' )
20 .end
Lines 7 and 20 bracket the main entry point to this test file–a
subroutine named main. The name is immaterial to everything
but programmers; only the :main attribute tells Parrot that,
when invoking this file directly, this is the entry point.
Lines 8 and 9 load two PIR libraries. One is the pure-PIR testing system
and the other is the file of FizzBuzz subroutines to test. Yes,
load_bytecode is an inopportune name.
Line 11 contains another variable declaration. It only exists to make
the .IMPORT() macro work correctly. This time, the variable is
a local variable–that is, it persists only through this compilation unit
(the subroutine). It holds a PMC–a Parrot Magic Cookie, Parrot’s single
non-primitive type.
Lines 12 through 14 import three testing functions from
Test::More. Though they come from a separate namespace,
they’re available within this test file as if I had declared them locally.
(Now you see why I used .IMPORT.)
Line 16 starts the testing by telling the test library that I plan to
run TESTS number of tests.
Lines 18 and 19 call a the remaining function in this file with the name of the various FizzBuzz functions in examples/pir/fizzbuzz.pir.
22 .sub 'test_function'
Note the lack of :main or any other attribute on line
22.
23 .param string func_name
Line 23 demonstrates how to access subroutine arguments in PIR. The
.param declaration is like .local in its scope,
but beyond merely associating a name to a particular type of register
within this compilation unit, it also accesses the appropriate value passed
into the subroutine.
25 .local pmc test_sub
26 test_sub = find_global [ 'FizzBuzz' ], func_name
With the function name passed in as an argument, this function needs to
be able to access the function in my example PIR file. The
find_global opcode takes a namespace key and a string and
returns the PMC stored in that namespace under that name. If I have the
name correct, test_sub will contain a Subroutine PMC that I
can invoke.
Parrot has first-class functions; this is more than just a function pointer.
27 diag( func_name )
Line 27 calls a Test::More function to show the name of the
function being tested. It won’t interfere with the TAP output in any way.
It’s just nice when running the tests directly for diagnostic purposes.
29 .local pmc results
30 results = test_sub( 10 )
31
32 .local int count
33 count = results
Lines 29 and 32 declare two variables which I used throughout the
remainder of this function. results contains an Array PMC
returned from the subroutine being tested. I use count to hold
the number of elements in the results PMC. (If it’s not clear, the count = results line ends up calling the get_integer() vtable method on the PMC. That returns the number of elements for an Array-like PMC. How does Parrot know to call that method? count is an integer, and the assignment operation with an integer variable as an lvalue on a PMC rvalue turns into the get_integer() call.)
All of my FizzBuzz subroutines currently use the same interface. They take an integer for the number of elements to create and return an Array PMC containing the strings for each element. This makes them all easier to test.
34 is( count, 10, 'test function should return the correct number of results' )
The first test is that calling the subroutine being tested and asking for ten elements should produce an Array PMC containing ten elements.
36 results = test_sub( 100 )
37 count = results
38 is( count, 100, '... based on start and end' )
That didn’t quite meet the FizzBuzz challenge though, so I tried it
again with 100 elements. (I also worked in a very small step
when developing the previous step; I hardcoded the tested subroutine to
return an Array of 10 null elements.)
40 .local string element
41 element = results[0]
42 is( element, '', '... with nothing for the first element' )
43
44 element = results[2]
45 is( element, 'Fizz', '... with Fizz for the third element' )
46
47 element = results[4]
48 is( element, 'Buzz', '... and Buzz for the fifth element' )
49
50 element = results[14]
51 is( element, 'FizzBuzz', '... and FizzBuzz for the fifteenth element' )
52
53 element = results[17]
54 is( element, 'Fizz', '... and Fizz for the eighteenth element' )
55
56 element = results[19]
57 is( element, 'Buzz', '... and Buzz for the twentieth element' )
58
59 element = results[29]
60 is( element, 'FizzBuzz', '... and FizzBuzz for the thirtieth element' )
This is the heart of the test now. With the returned
results array, accessing individual elements should give the
right answers for the FizzBuzz challenge. You’ve probably noticed, however,
that all of the numbers are off by one. This is because Parrot’s array
indices start at 0, not 1. I could have padded the array by one element if
this were important, but noting it explicitly seemed sufficient.
61 .end
Finally, this test function needs to end.
That’s the test code. It doesn’t yet meet the FizzBuzz challenge, and that’s not only because I haven’t shown the code for the tested subroutines yet. This doesn’t print anything, unless you consider test output.
Here’s the first part of examples/pir/fizzbuzz.pir:
1 .namespace [ 'FizzBuzz' ]
The .namespace directive tells Parrot that all subsequent
declarations should take place into a separate namespace. That’s
unnecessary for simple example code, but it’s a good habit to avoid name
clashes. (I wanted to write as real a program as possible.
3 .sub 'main' :main
4 .param pmc argv
Like the test file, this code has its own main function as
well. What happens when Parrot loads it from the test file? Absolutely
nothing. Only the first :main is the main.
However, this means that you can run this program on its own, because in
that case this main function will execute. Line
4 gives access to the command-line arguments provided in that case.
6 .local string sub_name
7 sub_name = argv[1]
8
9 if sub_name goto load_sub
10 sub_name = 'procedural'
The first element of argv is the program name. That’s
immaterial here. The real arguments start at index 1. I expect
this program to take one argument, the name of the testable subroutine to
run. If there’s no subroutine provided, default to
procedural.
Line 9 might look a little awkward. Yes, goto is the main
form of intrafunction control flow in Parrot. Remember that I called it an
assembly language.
12 load_sub:
13 .local pmc sub_pmc
14 sub_pmc = find_global sub_name
This code ought to look familiar; it uses the string name of a
subroutine to (attempt to) fetch the associated Sub PMC. I say
attempt because someone may provide an invalid name. I didn’t add
any error handling to check for this condition, but the best approach is to
make sure that sub_pmc contains a PMC that implements the Sub
role somehow.
Note that this use of find_global is different; I’m calling
it here from the same namespace where I’ve declared the testable
subroutines, so I don’t need the namespace key that I used in the test
file.
16 .local pmc results
17 results = sub_pmc( 100 )
18
19 .local pmc iter
20 iter = new .Iterator, results
21
22 .local string elem
23 .local int count
24 count = 1
Lines 16 and 17 should look familiar.
Lines 19 and 20 create an iterator. This is another type of PMC, used to iterate through an aggregate.
Lines 22 through 24 declare a couple of variables I want to use in the upcoming loop.
26 iter_start:
27 unless iter goto iter_end
28 elem = shift iter
29
30 print count
31 print ": "
32 print elem
33 print "\n"
34
35 inc count
36 goto iter_start
Lines 26 through 28 are boilerplate for every use of a Parrot iterator that I’ve ever seen. 26 is the start of loop label, 27 is the end of loop condition, and 28 gets the current element out of the iterator into a PMC variable.
Lines 30 through 33 print the number of the current element and the
current element. If it’s blank, it’s blank. Otherwise, it contains
Fizz, Buzz, or FizzBuzz.
Line 35 increments the count variable, used for display purposes only (it started at one). Line 36 restarts the loop.
38 iter_end:
39 end
40 .end
The loop and the function end here.
The rest of the code actually does the important FizzBuzz work. I’ll
show that tomorrow. In the meantime, I wonder how many people can build
Parrot (or just read the documentation) and write their own version of
these functions. procedural is easy, but
keyed_array took a few minutes.


The link to Parrotcode is .com where it should be .org
Thanks, Tyxod. I just fixed that and clarified a piece of code!
In PHP, fast as I can make it (and without formatting because of the way comments are handled):
for($i=1;$i<101;++$i)
{
echo((($i%3==0)&&($i%5==0)?'fizzbuzz':(($i%3==0?'fizz':((($i%5==0?'buzz':$i)))))));
}
Mohammad,
Forgive me if I'm off base here. I'm not sure if you're just pointing out another solution for comparison or if you've missed the point. The article isn't about conciseness of code. Parrot is meant to be low-level. Perl is a higher-level language which can make use of Parrot as a backend (and will). The development of Perl 6 and Parrot are intertwined and complementary, but the two are quite different languages.
If you really want a short program, Perl 5 easily does that:
print$_%15?$_%5?$_%3?$_:'fizz':'buzz':'fizzbuzz',$/for 1..100
However, clarity suffers a bit and it's possible to write it much more clearly in the same language. Over on Perlmonks.org the gauntlet was thrown to get this program as short as possible, and 63 characters would be far from the winner in that challenge. Clarity suffers even more in many of those solutions, though.
Clarity also suffers in this golfed Ruby program (from contestants of a similar challenge at Ruby-Forum.com):
1.upto(?d){|i|i%3<1&&x=:Fizz;puts i%5<1?"#{x}Buzz":x||i}
Perl and Ruby are much more directly comparable to PHP than is Parrot.