Juraj's Blog

11 May 2020

Programming Fizz-Buzz in Forth

After finished implementing my Forth interpreter I was finally ready to do some programming in Forth.

Let’s do the famous Fizz-Buzz test .

The assignment goes: “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”."

The cascading ifs

Let’s do a naive one-word implementation. Assuming the pseudocode:

def fizzbuzz(i):
    if(i%3==0 and i%5==0):
        print "fizzbuzz" 
    else:
        if(i%3==0):    
            print "fizz"
        else:
            if(i%5==0):
                print "buzz" 
            else:
                print i

Converts straightforwardly into Forth as follows:

: fizzbuzz
    \ is n divisible by 3 and 5?
    dup dup 3 mod 0= swap 5 mod 0= and 
    if ." fizzbuzz " drop 
    \ divisible by 3?
    else dup 3 mod 0= 
        if ." fizz" drop
        \ divisible by 5?
        else dup 5 mod 0=
            if ." buzz" drop
            else .
            then
        then
    then
;

We do dup a lot to keep the original argument on the stack for the modulo 0 comparisons.

Note that we clean up the stack after each printout as we keep the argument around if it gets to . eventually in case not fizz, neither buzz got printed.

Maybe extracting a little helper word for the check for n mod m (the repeating dup N mod 0=? sequence):

: 0mod 2dup mod 0= nip ; ok
: fizzbuzz 3 0mod swap 5 0mod and 
    if ." fizzbuzz " drop
    else 3 0mod
        if ." fizz" drop
        else 5 0mod
            if ." buzz" drop
            else .
            then
        then
    then
;

Nope, it’s really not more readable.

A more stacky way

How about we introduce the fizz and buzz words that will print out fizz/buzz respectively and also push a boolean on the stack to indicate whether they succeeded?

Forth:

: fizz 3 mod 0= if ." fizz" true else false then ;
: buzz 5 mod 0= if ." buzz" true else false then ;

Then we can execute both checks in sequence them into the helper (fizzbuzz) word

: (fizzbuzz) dup dup fizz swap buzz or if drop else . then ;
: fizzbuzz do i (fizzbuzz) cr loop ; 
20 1 fizzbuzz

Pseudocode:

def fizz(i):
    if(i%3==0):
        print "fizz"
        return True
    else:
        return false

def buzz(i):
    ...

def fizzbuzz(i):
    if not (fizz(i) and buzz(i)):
        print i

Golfing it

Starting with 200 characters

: fizz 3 mod 0= if ." fizz" true else false then ;
: buzz 5 mod 0= if ." buzz" true else false then ;
: (fizzbuzz) dup dup fizz swap buzz or if drop else . then ;
: fizzbuzz do i (fizzbuzz) cr loop ; 

Let’s just rename symbols first -> 163

: f 3 mod 0= if ." fizz" true else false then ;
: b 5 mod 0= if ." buzz" true else false then ;
: g dup dup f swap b or if drop else . then ;
: h do i g cr loop ; 

inline the old (fizzbuzz) aka g -> 155

: f 3 mod 0= if ." fizz" true else false then ;
: b 5 mod 0= if ." buzz" true else false then ;
: h do i dup dup f swap b or if drop else . then cr loop ; 

Do away with true/false -> 143 characters

: f 3 mod 0= if ." fizz" -1 else 0 then ;
: b 5 mod 0= if ." buzz" -1 else 0 then ;
: h do i dup dup f swap b or if drop else . then cr loop ;