← Code Compare

Ranges and Iteration

Two everyday loops side by side: iterating a numeric range, and walking a collection while tracking the index of each element. Each language ships its own idiom, from C-style index loops to range objects, enumerate-style pairing, and key/value iterators.

Show: GujiGoOCamlHaskellPerlRakuRustPython
Guji
sub main() {
    for $n in 1 .. 3 {
        print("n=$n")
    }
    @fruits = ["apple", "banana", "cherry"]
    for $i in 0 ..< @fruits.count() {
        print("$i: { @fruits[$i] }")
    }
}

1 .. 3 is an inclusive Int range and 0 ..< @fruits.count() is a half-open one, both iterable by for (spec 5.4, 6.3). The index loop reads each element back with @fruits[$i], keeping the @ sigil on access. Strings interpolate $n and $i directly, while { ... } interpolates the indexing expression.

Go
package main

import "fmt"

func main() {
	for n := 1; n <= 3; n++ {
		fmt.Printf("n=%d\n", n)
	}
	fruits := []string{"apple", "banana", "cherry"}
	for i, name := range fruits {
		fmt.Printf("%d: %s\n", i, name)
	}
}

Go has no range literal, so a numeric loop is the classic three-clause for. Iterating a slice uses for i, name := range, which yields the index and a copy of each element together. Printf with verbs like %d and %s formats each line.

OCaml
let () =
  for n = 1 to 3 do
    Printf.printf "n=%d\n" n
  done;
  let fruits = [| "apple"; "banana"; "cherry" |] in
  Array.iteri (fun i name -> Printf.printf "%d: %s\n" i name) fruits

OCaml's for n = 1 to 3 do ... done is its built-in inclusive integer loop. Array.iteri passes both the index and element to a callback, the idiomatic way to walk a collection with its position. Printf.printf supplies typed formatting for each line.

Haskell
import Text.Printf (printf)

main :: IO ()
main = do
  mapM_ (\n -> printf "n=%d\n" n) [1 .. 3 :: Int]
  let fruits = ["apple", "banana", "cherry"]
  mapM_ (\(i, name) -> printf "%d: %s\n" i name) (zip [0 :: Int ..] fruits)

Haskell expresses a range as the lazy list [1 .. 3] and runs an effect over each element with mapM_. Pairing a list with indices is zip [0 ..], producing (index, element) tuples that the lambda destructures. There is no mutable counter; iteration is just consuming a list.

Perl
use strict;
use warnings;

print "n=$_\n" for 1 .. 3;

my @fruits = ("apple", "banana", "cherry");
for my $i (0 .. $#fruits) {
    print "$i: $fruits[$i]\n";
}

The .. range operator drives a statement-modifier loop, with each value bound to the topic $_. For the indexed walk, 0 .. $#fruits ranges over the array's index bounds, and $fruits[$i] fetches each element. Variables interpolate straight into the double-quoted strings.

Raku
say "n=$_" for 1 .. 3;

my @fruits = <apple banana cherry>;
for @fruits.kv -> $i, $name {
    say "$i: $name";
}

Raku iterates the 1 .. 3 Range with a for statement modifier, binding each value to $_. The .kv method flattens a list into index/value pairs, which the pointy block -> $i, $name takes two at a time. say prints each line with a trailing newline.

Rust
fn main() {
    for n in 1..=3 {
        println!("n={}", n);
    }
    let fruits = ["apple", "banana", "cherry"];
    for (i, name) in fruits.iter().enumerate() {
        println!("{}: {}", i, name);
    }
}

Rust's 1..=3 is an inclusive range and 1..4 a half-open one, both iterators usable directly in for. Calling .enumerate() on an iterator yields (index, item) tuples that the loop pattern destructures. println! interpolates each value into the {} placeholders.

Python
def main():
    for n in range(1, 4):
        print(f"n={n}")
    fruits = ["apple", "banana", "cherry"]
    for i, name in enumerate(fruits):
        print(f"{i}: {name}")

main()

range(1, 4) is a half-open range object that yields 1, 2, 3 lazily. enumerate(fruits) pairs each element with its index, unpacked into i and name by the loop. F-strings interpolate the values inline for printing.