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.