Lab · Playground
FizzBuzz in nineteen languages
The smallest interesting program, written the way each language would write it — from JavaScript and Rust to SQL, Prolog, BQN, Forth and a Befunge maze. Every block runs and prints the same twenty lines.
FizzBuzz counts from 1 to 20, but says Fizz for multiples of three, Buzz for multiples of five, and FizzBuzz for multiples of both. It is too small to be a test of anything, which is what makes it a good tour: with the problem out of the way, what is left is how each language likes to say "pick one of four things".
Every block below prints the same twenty lines. Press Run on any of them, then change it — make it go to 100, add Bazz for sevens, and see which languages make that easy. Rust, Go and Kotlin are compiled by their official online playgrounds; everything else runs in your browser, so the first run of a language downloads its runtime.
The mainstream
const fizzbuzz = n =>
(n % 3 ? '' : 'Fizz') + (n % 5 ? '' : 'Buzz') || String(n);
for (let n = 1; n <= 20; n++) console.log(fizzbuzz(n));
Two tricks in one line: n % 3 is falsy exactly when it is zero, and || falls back to the
number when both halves are empty strings. Gloriously terse; also the reason JavaScript has a
reputation.
def fizzbuzz(n):
word = "Fizz" * (n % 3 == 0) + "Buzz" * (n % 5 == 0)
return word or str(n)
print("\n".join(fizzbuzz(n) for n in range(1, 21)))
The same idea said politely: a bool is an int, so "Fizz" * True is "Fizz", and an empty
string is falsy. range(1, 21) is the half-open interval Python uses everywhere.
#include <stdio.h>
int main(void) {
for (int n = 1; n <= 20; n++) {
if (n % 15 == 0) puts("FizzBuzz");
else if (n % 3 == 0) puts("Fizz");
else if (n % 5 == 0) puts("Buzz");
else printf("%d\n", n);
}
return 0;
}
No strings to glue together without thinking about memory, so C does the obvious thing: test 15 first and pick a line. This is the version most interviewers expect.
fn fizzbuzz(n: u32) -> String {
match (n % 3, n % 5) {
(0, 0) => "FizzBuzz".to_string(),
(0, _) => "Fizz".to_string(),
(_, 0) => "Buzz".to_string(),
_ => n.to_string(),
}
}
fn main() {
for n in 1..=20 {
println!("{}", fizzbuzz(n));
}
}
Match on a tuple of remainders and the compiler checks that the four cases cover everything. The
.to_string() calls are Rust being honest that literals and formatted numbers are different
kinds of string.
package main
import (
"fmt"
"strconv"
)
func fizzbuzz(n int) string {
switch {
case n%15 == 0:
return "FizzBuzz"
case n%3 == 0:
return "Fizz"
case n%5 == 0:
return "Buzz"
}
return strconv.Itoa(n)
}
func main() {
for n := 1; n <= 20; n++ {
fmt.Println(fizzbuzz(n))
}
}
Go’s bare switch is an if/else if chain that reads like a table. No ternary operator, no
cleverness — Go would rather you wrote the obvious thing.
fun fizzbuzz(n: Int) = when {
n % 15 == 0 -> "FizzBuzz"
n % 3 == 0 -> "Fizz"
n % 5 == 0 -> "Buzz"
else -> n.toString()
}
fun main() {
(1..20).map(::fizzbuzz).forEach(::println)
}
when is an expression, so the function is a single =. Ranges and function references make
the main loop a pipeline.
fizzbuzz = ->(n) do
case [n % 3, n % 5]
in [0, 0] then "FizzBuzz"
in [0, _] then "Fizz"
in [_, 0] then "Buzz"
else n.to_s
end
end
puts (1..20).map(&fizzbuzz)
Ruby 3’s pattern matching (case … in) does what Rust’s match does. puts given an array
prints one element per line, and &fizzbuzz turns the lambda into a block.
for n = 1, 20 do
local word = (n % 3 == 0 and "Fizz" or "") .. (n % 5 == 0 and "Buzz" or "")
print(word ~= "" and word or n)
end
Lua has no ternary either, so cond and a or b stands in for one. The catch: in Lua 0 is true,
so the remainders have to be compared with 0 explicitly — the JavaScript trick would print
FizzBuzz twenty times.
<?php
foreach (range(1, 20) as $n) {
echo match (true) {
$n % 15 === 0 => 'FizzBuzz',
$n % 3 === 0 => 'Fizz',
$n % 5 === 0 => 'Buzz',
default => $n,
}, "\n";
}
PHP 8’s match is strict and an expression; match (true) is the idiom for "the first arm whose
condition holds".
Functional and logical
let fizzbuzz n =
match n mod 3, n mod 5 with
| 0, 0 -> "FizzBuzz"
| 0, _ -> "Fizz"
| _, 0 -> "Buzz"
| _ -> string_of_int n
let () = List.iter (fun n -> print_endline (fizzbuzz n)) (List.init 20 succ)
Where Rust’s match came from. List.init 20 succ builds [1; …; 20] by applying the successor
function to 0 … 19.
(defn fizzbuzz [n]
(or (not-empty (str (when (zero? (mod n 3)) "Fizz")
(when (zero? (mod n 5)) "Buzz")))
n))
(run! println (map fizzbuzz (range 1 21)))
when returns nil when its test fails and str treats nil as empty, so the word builds
itself; not-empty turns "" into nil so or can fall back to the number. map is lazy;
run! forces it for the side effect.
(define (fizzbuzz n)
(cond ((= 0 (modulo n 15)) "FizzBuzz")
((= 0 (modulo n 3)) "Fizz")
((= 0 (modulo n 5)) "Buzz")
(else (number->string n))))
(let loop ((n 1))
(when (<= n 20)
(display (fizzbuzz n))
(newline)
(loop (+ n 1))))
A named let is Scheme’s loop: a local recursive function called in tail position, which the
language guarantees will not grow the stack.
fizzbuzz(N, 'FizzBuzz') :- 0 is N mod 15, !.
fizzbuzz(N, 'Fizz') :- 0 is N mod 3, !.
fizzbuzz(N, 'Buzz') :- 0 is N mod 5, !.
fizzbuzz(N, N).
fizzbuzz_to(Max) :-
forall(between(1, Max, N), (fizzbuzz(N, Word), writeln(Word))).
?- fizzbuzz_to(20).
Facts about what a number is called, tried in order; the cut (!) stops Prolog from going on
to find the other, also-true answers (15 is a multiple of 3, after all). forall/2 runs the
inner goal for every solution of between/3.
Arrays, data and stacks
WITH RECURSIVE n(i) AS (
SELECT 1
UNION ALL
SELECT i + 1 FROM n WHERE i < 20
)
SELECT CASE
WHEN i % 15 = 0 THEN 'FizzBuzz'
WHEN i % 3 = 0 THEN 'Fizz'
WHEN i % 5 = 0 THEN 'Buzz'
ELSE i
END AS fizzbuzz
FROM n;
SQL has no loops, but a recursive common table expression generates the numbers as rows, and a
CASE maps each one. The answer is a table, as everything in SQL is.
n <- 1:20
out <- as.character(n)
out[n %% 3 == 0] <- "Fizz"
out[n %% 5 == 0] <- "Buzz"
out[n %% 15 == 0] <- "FizzBuzz"
cat(out, sep = "\n")
No loop at all: R works on whole vectors, and a logical vector used as an index picks the
elements to overwrite. The order of the three assignments does the job of the if chain.
FizzBuzz ← {
words ← ∾ (0 = 3‿5 | 𝕩) / "Fizz"‿"Buzz"
(0 < ≠words) ◶ ⟨•Fmt 𝕩, words⟩ @
}
lines ← FizzBuzz¨ 1 + ↕20
•Out¨ lines
≠lines
An array language does FizzBuzz as a filter: 3‿5 | 𝕩 is both remainders at once, 0 = turns
them into a mask, and / keeps the words the mask selects. ◶ picks between the number and the
words. BQN prints a program’s final value, so the last line is the number of lines printed.
: fizz? ( n -- f ) 3 mod 0= ;
: buzz? ( n -- f ) 5 mod 0= ;
: fizzbuzz ( n -- )
dup fizz? over buzz? or
if dup fizz? if ." Fizz" then
buzz? if ." Buzz" then
else .
then cr ;
: run 21 1 do i fizzbuzz loop ;
run
Everything goes through the stack, and the comments in parentheses are the stack effect of each
word. ." prints a string, . prints a number (with a trailing space, as Forth does).
10 FOR N = 1 TO 20
20 W$ = ""
30 IF N MOD 3 = 0 THEN W$ = "Fizz"
40 IF N MOD 5 = 0 THEN W$ = W$ + "Buzz"
50 IF W$ = "" THEN W$ = STR$(N)
60 PRINT W$
70 NEXT N
How a lot of us first wrote it, on a machine with a flashing cursor. STR$ is used rather than
PRINT N because BASIC prints a number with a space in front, where a minus sign would go.
1>:35*%#v_"zzuBzziF",,,,,,,, v
>:3%#v_"zziF",,,, v
>:5%#v_"zzuB",,,, v
>:. v
@
|`*54:+1,+55<
^ <
The program is a grid and the instruction pointer walks it. It starts top left going right with
1 on the stack; each _ pops a remainder and sends the pointer right (to print a word) if it was
zero or left, onto a v and down to the next test. The words are pushed backwards ("zziF") so
they pop off in order. Every path ends at the right-hand column, which falls to the bottom row:
print a newline (55+,), add one, and compare with 20 (54*) — go up to @ and stop, or down and
round to the ^ and start again.
What it shows
| Approach | Languages |
|---|---|
Test 15 first, pick a line |
C, Go, Kotlin, PHP, Scheme, Prolog, SQL |
Glue "Fizz" and "Buzz" together, fall back to the number |
JavaScript, Python, Lua, Clojure, BQN, Forth, BASIC |
Pattern-match a pair of remainders |
Rust, OCaml, Ruby |
Whole vectors, no loop |
R, BQN, SQL |
Geometry |
Befunge |
The "glue" versions are the ones that survive change: adding Bazz for sevens is one more term,
where the "test 15 first" versions need 21, 35 and 105 as well. The pattern-matching versions sit
in between — one more element in the tuple, and the compiler (in Rust and OCaml) tells you which
cases you have not covered. Notice too how many languages had to work around "zero is false":
JavaScript and Python lean on it, Lua cannot, and Forth’s 0= is the honest version of both.
All nineteen print exactly the same twenty lines — scripts/check-rosetta.mjs in the site’s repository runs them all in a headless browser and
compares.