Loops in Go - a Cheat Sheet

This is the first in my “Cheat Sheet” series, which provide a side-by-side view of the idiomatic way to write a piece of code in the languages I know well enough to do so. Each example prints the same output in all of the languages shown.

Iterating over values

iterating over a numeric range

for i in xrange(5):
   print i
for my $i (0..4) {
    say $i;
}
for i := 0; i < 5; i++ {
    println(i)
}

  • iterating backwards over a numeric range
for i in xrange(5, 0, -1):
    print i
for (my $i = 4; $i >= 0; $i--) {
    say $i;
}
for i := 5; i >= 0; i-- {
    prinln(i)
}

Iterating over collections

  • iterating over a slice/array
for x in my_list:
    print x
for my $v (@array) {
    say $v;
}
for _, v := range myList {
    println(v)
}

This one is easy to get caught out on: the ‘range’ expression gives you indices, not values when it is passed a list.

  • iterating over an array with indices:
for i in xrange(len(my_list)):
    v = my_list[i]
    print "%d: %s" % (i, v)
for (my $i = 0; $i <= $#array; $i++) {
    my $v = $array[$i];
    say $i, " ", $v;
}
for i, v := range myList {
    println(i, v)
}
  • iterating over keys of a map
for k in my_dict:
    print k
while (my $k = each %hash) {
    say $k;
}
for k := range myMap {
    println(k)
}
  • iterating over values of a map
for v in my_dict.values():
    print v
for my $v (values %hash) {
    say $v;
}
for _, v := range myMap {
    println(v)
}
  • iterating over keys and values of a map
for k, v in my_dict.items():
    print "%s %s" % (k, v)
while (my ($k, $v) = each %hash) {
    say $k, " ", $v;
}
for k, v := range myMap {
    println(k, v)
}

Conditional Loops

  • loops with a pre-condition
i = 5
while i > 0:
    print i
    i -= 1
my $i = 5;
while ($i > 0) {
    say $i;
    $i--;
}
i := 5
for i > 0 {
    println(i)
    i--
}
  • loops with a post-condition
i = 5
done = False
while not done:
    print i
    i -= 1
    done = (i <= 0)
my $i = 5;
do {
    say $i;
    $i--;
} until ($i <= 0);
i := 5
done := false
for !done {
    println(i)
    i--
    done = (i <= 0)
}

Neither python nor go have an explicit post-condition loop statement, so you have to fake it with a temporary.

Loop flow control

The loops in this section are all actually “goto in disguise”, so don’t think one language is more ugly than the other if they spell the keyword ‘goto’.

  • skipping to the next iteration
i = 5
while i > 0:
    i -= 1
    if i == 2:
        continue
    print i
my $i = 5;
while ($i > 0) {
    $i--;
    next if $i == 2;
    say $i;
}
i := 5
for i > 0 {
    i--
    if i == 2 {
        continue
    }
    println(i)
}
  • exiting loop early
i = 5
while i > 0:
    i -= 1
    if i == 2:
        break
    print i
my $i = 5;
while ($i > 0) {
    $i--;
    last if $i == 2;
    say $i;
}
i := 5
for i > 0 {
    i--
    if i == 2 {
        break
    }
    println(i)
}
  • outer nested loop control
for i in xrange(5):
    exit_outer = False
    for j in xrange(5):
        product = i * j
        print "%d x %d = %d" % (i, j, product)
        if product == 12:
            exit_outer = True
            break
    if exit_outer:
        break
outer:
    for my $i (0..4) {
        for my $j (0..4) {
            my $product = $i * $j;
            say $i, " x ", $j, " = ", $product;
            last outer if $product == 12;
        }
    }
outer: for i := 0; i < 5; i++ {
    for j := 0; j < 5; j++ {
        product := i * j
        fmt.Println("%d x %d = %d", i, j, product)
        if product == 12 {
            break outer
        }
    }
}

Python does not support control of the outer loop, so you have to explicitly declare a variable which is used to terminate it.

Looping over the progress of a function

  • iterating over generator functions
def generator():
    for i in xrange(5):
        yield i

for i in generator():
    print i
sub get_generator {
    my $i = 0;
    return sub {
        if ($i < 5) {
            return $i++;
        }
    };
}
my $generator = get_generator()
while (defined(my $i = $generator->())) {
    say $i;
}
func generator() (ch chan int) {
    go func {
        for i := 0; i < 5; i++ {
            ch <- i
        }
    }
    return
}
for i := range generator() {
    println(i)
}

Perl doesn’t really have generator functions; it has no ‘yield’ statement without unstable out-of-core extensions. There are multiple approaches to generators in Perl, and the above is the approach from “Higher Order Perl”.

Idiomatic go code tends to use goroutines where other languages use iterators.

Goofy looping

  • python’s for “else” keyword
for i in xrange(5):
    if i * i == 17:
        break
    print i
else:
print "17 is not a square"
my $broke;
for my $i (0..4) {
    if ($i * $i == 17) {
        $broke = 1;
        last;
    }
    say $i;
}
unless $broke {
    say "17 is not a square"
}
broke := false
for i := 0; i < 5; i++ {
    if i * i == 17 {
        broke = true
        break
    }
    println(i)
}
if !broke {
    println("17 is not a square")
}

Python’s flow control statements seem to randomly have ‘else’ on them in places, which run if the block they appear after exits normally. Neither perl nor go supports this construct, requiring the use of a flag variable.

  • perl’s “redo” statement
i = 5
while i > 0:
    did_it = False
    while not did_it:
        did_it = True
        print i
        i -= 2
        if i == 1:
            i = 4
            did_it = False
my $i = 5;
while ($i > 0) {
    say $i;
    $i -= 2;
    if ($i == 1) {
        $i = 4;
        redo;
    }
}
i := 5
for i > 0 {
top:
    println(i)
    i -= 2
    if i == 1 {
        i = 4
        goto top
    }
}

Perl has a ‘redo’ statement which skips to the top of the iteration without evaluating the conditional. You can write this as a ‘goto’ in Go. In Python it is especially awkward.

Share Comments
comments powered by Disqus