Chris Collins

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.

Code blocks on this page are editable — change them and press Run.

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

JavaScript
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.

Python
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.

C
#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.

Rust
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.

Go
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.

Kotlin
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.

Ruby
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.

Lua
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
<?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

OCaml
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.

Clojure
(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.

Scheme
(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.

Prolog
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

SQL (SQLite)
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.

R
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.

BQN
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.

Forth
: 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).

Classic BASIC
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.

Befunge-93
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.