FizzBuzz Obsessions I: An introduction, and some softcore Functional Reactive FizzBuzzing
Background
I suddenly remembered reading that a great percentage of would-be programmers simply cannot write a FizzBuzz program in any language. After going "how terrible, all those people!", I wondered: well... can I?
I had a vague impression of maybe having done it once. This vagueness annoyed me: what if I belonged to the vast masses of non-FizzBuzzers?
So I had to find out. The task was not beneath me. Surely I could spend 10 minutes on it, yes?
Ha!
No, the issue wasn't that it took me too long to finish the task in a language I'm comfortable with (my first correct fizzbuzz.sh
did take me under 10 minutes). The issue was that I kept asking "what about...?" and "what if...?", unsatisfied that I was with just any old haphazardly put-together fizzbuzz thing.
So I had to do something about it.
I started "small".
Then I tried my hand at two well-known functional languages.
Then I tried something else — and things went wild.
But I'm getting ahead of myself. Let's start with the "starting small".
Note: Although FizzBuzz is "officially" up to 100, I will often use shorter results for the examples.
Warm-up: FizzBuzz in 2½ functional reactive languages — without lenses or bananas
Spreadsheets. This is what I'm talking about.
- They are functional: you put an equal sign with a formula, and it operates on other cells without side-effects.
- They are reactive: you update some cell and BAM!, formulas are updated elsewhere immediately.
- They are, however, usually free of bananas. Also no lenses or barbed wire. Bummer, eh?
(fruitful link here [pdf] or here [pdf] in case you have no clue what I'm talking about).
So here we go.
FizzBuzz in Libre Office Calc
Surprisingly, it took me less than 5 minutes. Had been a while since I last composed spreadsheet formulas, but I still remembered the simple syntax.
A | B | |
---|---|---|
1 | 1 | =IF(MOD(A1,5)<>0,IF(MOD(A1,3)<>0,A1,"Fizz"),IF(MOD(A1,3)<>0,"Buzz","FizzBuzz")) |
2 | 2 | =IF(MOD(A2,5)<>0,IF(MOD(A2,3)<>0,A2,"Fizz"),IF(MOD(A2,3)<>0,"Buzz","FizzBuzz")) |
… | … | … |
...and drag it down.
That's all.
(It should work identically in MS Excel.)
FizzBuzz in Simple Emacs Spreadsheet
Now we can try it in SES: Simple Emacs Spreadsheet.
What?! You didn't know Emacs had spreadsheets? Of course it does. What doesn't Emacs have?
That was also straightforward:
- First create an empty file in a terminal:
>fizzbuzz.ses
- Then open it in Emacs. There: a spreadsheet.
- M-o (Alt+o) to add a second column. C-o (Ctrl+o) 19 times to add 19 rows.
- Then RET 1 RET on cell A1 (RET = Return = Enter).
- Then RET
(cond ((zerop (% A1 15)) "FizzBuzz") ((zerop (% A1 3)) "Fizz") ((zerop (% A1 5)) "Buzz") (t A1))
RET on cell B1. - Still on B1, w 10 for more width.
- Select first row with the usual C-SPC to mark A1, right, then C-SPC to release. Copy it.
- Paste it (yank it) below.
- Then RET
(1+ A1)
RET on A2. - Select and copy second row.
- Paste it (yank it) to other lines. Surprise! The formula follows it.
- C-c C-p then
("%.7g")
: left-align all cells.
Widening to view the data area, it looks like the below.
Save it as fizzbuzz.ses
. Open it in your Emacs. And there you have it.
Press RET on any cell to see the formula.
1 1 2 2 3 Fizz 4 4 5 Buzz 6 Fizz 7 7 8 8 9 Fizz 10 Buzz 11 11 12 Fizz 13 13 14 14 15 FizzBuzz 16 16 17 17 18 Fizz 19 19 20 Buzz
(ses-cell A1 1 nil nil (A2 B1)) (ses-cell B1 1 (cond ((zerop (% A1 15)) "FizzBuzz") ((zerop (% A1 3)) "Fizz") ((zerop (% A1 5)) "Buzz") (t A1)) nil nil) (ses-cell A2 2 (1+ A1) nil (A3 B2)) (ses-cell B2 2 (cond ((zerop (% A2 15)) "FizzBuzz") ((zerop (% A2 3)) "Fizz") ((zerop (% A2 5)) "Buzz") (t A2)) nil nil) (ses-cell A3 3 (1+ A2) nil (A4 B3)) (ses-cell B3 "Fizz" (cond ((zerop (% A3 15)) "FizzBuzz") ((zerop (% A3 3)) "Fizz") ((zerop (% A3 5)) "Buzz") (t A3)) nil nil) (ses-cell A4 4 (1+ A3) nil (A5 B4)) (ses-cell B4 4 (cond ((zerop (% A4 15)) "FizzBuzz") ((zerop (% A4 3)) "Fizz") ((zerop (% A4 5)) "Buzz") (t A4)) nil nil) (ses-cell A5 5 (1+ A4) nil (A6 B5)) (ses-cell B5 "Buzz" (cond ((zerop (% A5 15)) "FizzBuzz") ((zerop (% A5 3)) "Fizz") ((zerop (% A5 5)) "Buzz") (t A5)) nil nil) (ses-cell A6 6 (1+ A5) nil (A7 B6)) (ses-cell B6 "Fizz" (cond ((zerop (% A6 15)) "FizzBuzz") ((zerop (% A6 3)) "Fizz") ((zerop (% A6 5)) "Buzz") (t A6)) nil nil) (ses-cell A7 7 (1+ A6) nil (A8 B7)) (ses-cell B7 7 (cond ((zerop (% A7 15)) "FizzBuzz") ((zerop (% A7 3)) "Fizz") ((zerop (% A7 5)) "Buzz") (t A7)) nil nil) (ses-cell A8 8 (1+ A7) nil (A9 B8)) (ses-cell B8 8 (cond ((zerop (% A8 15)) "FizzBuzz") ((zerop (% A8 3)) "Fizz") ((zerop (% A8 5)) "Buzz") (t A8)) nil nil) (ses-cell A9 9 (1+ A8) nil (A10 B9)) (ses-cell B9 "Fizz" (cond ((zerop (% A9 15)) "FizzBuzz") ((zerop (% A9 3)) "Fizz") ((zerop (% A9 5)) "Buzz") (t A9)) nil nil) (ses-cell A10 10 (1+ A9) nil (A11 B10)) (ses-cell B10 "Buzz" (cond ((zerop (% A10 15)) "FizzBuzz") ((zerop (% A10 3)) "Fizz") ((zerop (% A10 5)) "Buzz") (t A10)) nil nil) (ses-cell A11 11 (1+ A10) nil (A12 B11)) (ses-cell B11 11 (cond ((zerop (% A11 15)) "FizzBuzz") ((zerop (% A11 3)) "Fizz") ((zerop (% A11 5)) "Buzz") (t A11)) nil nil) (ses-cell A12 12 (1+ A11) nil (A13 B12)) (ses-cell B12 "Fizz" (cond ((zerop (% A12 15)) "FizzBuzz") ((zerop (% A12 3)) "Fizz") ((zerop (% A12 5)) "Buzz") (t A12)) nil nil) (ses-cell A13 13 (1+ A12) nil (A14 B13)) (ses-cell B13 13 (cond ((zerop (% A13 15)) "FizzBuzz") ((zerop (% A13 3)) "Fizz") ((zerop (% A13 5)) "Buzz") (t A13)) nil nil) (ses-cell A14 14 (1+ A13) nil (A15 B14)) (ses-cell B14 14 (cond ((zerop (% A14 15)) "FizzBuzz") ((zerop (% A14 3)) "Fizz") ((zerop (% A14 5)) "Buzz") (t A14)) nil nil) (ses-cell A15 15 (1+ A14) nil (A16 B15)) (ses-cell B15 "FizzBuzz" (cond ((zerop (% A15 15)) "FizzBuzz") ((zerop (% A15 3)) "Fizz") ((zerop (% A15 5)) "Buzz") (t A15)) nil nil) (ses-cell A16 16 (1+ A15) nil (A17 B16)) (ses-cell B16 16 (cond ((zerop (% A16 15)) "FizzBuzz") ((zerop (% A16 3)) "Fizz") ((zerop (% A16 5)) "Buzz") (t A16)) nil nil) (ses-cell A17 17 (1+ A16) nil (A18 B17)) (ses-cell B17 17 (cond ((zerop (% A17 15)) "FizzBuzz") ((zerop (% A17 3)) "Fizz") ((zerop (% A17 5)) "Buzz") (t A17)) nil nil) (ses-cell A18 18 (1+ A17) nil (A19 B18)) (ses-cell B18 "Fizz" (cond ((zerop (% A18 15)) "FizzBuzz") ((zerop (% A18 3)) "Fizz") ((zerop (% A18 5)) "Buzz") (t A18)) nil nil) (ses-cell A19 19 (1+ A18) nil (A20 B19)) (ses-cell B19 19 (cond ((zerop (% A19 15)) "FizzBuzz") ((zerop (% A19 3)) "Fizz") ((zerop (% A19 5)) "Buzz") (t A19)) nil nil) (ses-cell A20 20 (1+ A19) nil (B20)) (ses-cell B20 "Buzz" (cond ((zerop (% A20 15)) "FizzBuzz") ((zerop (% A20 3)) "Fizz") ((zerop (% A20 5)) "Buzz") (t A20)) nil nil) (ses-column-widths [7 10]) (ses-column-printers [nil nil]) (ses-default-printer ("%.7g")) (ses-header-row 0) ( ;Global parameters (these are read first) 2 ;SES file-format 20 ;numrows 2 ;numcols ) ;; Local Variables: ;; mode: ses ;; End:
Note: The horizontal separator above, rendered in HTML, is a literal Ctrl-l, which you can insert in the file at that place with C-q C-l.
FizzBuzz in Org Tables
This one is not really reactive: you usually have to C-c C-c to reload formulas (though I'm sure there's some way to automate that). Hence the ½ in my title.
I expected it to be less trivial, but it was actually surprisingly fast to get it exactly right.
There are two ways to produce the formula:
- using Emacs Calc syntax (which, notice, is unrelated to Libre Office Calc)
- using Emacs Lisp syntax
I wrote it in both. They are below the table. Pressing C-c C-c on any of the two lines after pasting this into Emacs in Org Mode should return you the exact same table as result. You can try it from scratch, by deleting the contents of all cells except the first "1", after which one C-c C-c will produce the 1–20 column, and a second C-c C-c will correctly calculate the second column. Again, this should equally work the same on either formula line.
| FizzBuzz in Org tables | |----+----------| | 1 | 1 | | 2 | 2 | | 3 | Fizz | | 4 | 4 | | 5 | Buzz | | 6 | Fizz | | 7 | 7 | | 8 | 8 | | 9 | Fizz | | 10 | Buzz | | 11 | 11 | | 12 | Fizz | | 13 | 13 | | 14 | 14 | | 15 | FizzBuzz | | 16 | 16 | | 17 | 17 | | 18 | Fizz | | 19 | 19 | | 20 | Buzz | |----+----------| #+TBLFM: @1$1..@>$1=@-1+1::$2=if($1%15==0,FizzBuzz,if($1%3==0,Fizz,if($1%5==0,Buzz,$1))) #+TBLFM: @1$1..@>$1=@-1+1::$2='(cond ((zerop (% $1 15)) "FizzBuzz") ((zerop (% $1 3)) "Fizz") ((zerop (% $1 5)) "Buzz") (t $1));N
More about the syntax can be found here:
org#Formula syntax for Calc and org#Formula syntax for Lisp
And that was the end of my warm-up. Here's the second part.