January 2011 Archives

Using each() to iterate over an array

In perl 5.12, each() - as well as keys() and values() - works on arrays, where previously these functions were restricted to hashes. When iterating over an array, each() returns both the index and the value.

my @x = 50..59;
while(my ($i, $v) = each @x) {
    say "index $i, value $v";
}

will print:

index 0, value 50
index 1, value 51
index 2, value 52
index 3, value 53
index 4, value 54
index 5, value 55
index 6, value 56
index 7, value 57
index 8, value 58
index 9, value 59

Previously you could iterate over the array values but had to keep a counter yourself, or you could have a for-loop counting up, but then you had to retrieve the current array element separately. So this new construct saves quite a bit of code.

On a related note, if you need to iterate over an array, taking several elements at a time, you can use the well-known idiom:

while (my ($f, $g) = splice @x, 0, 2) {
    say "f $f, g $g";
}

which will print:

f 50, g 51
f 52, g 53
f 54, g 55
f 56, g 57
f 58, g 59

Caching multi-statement computations using an anonymous subroutine

Suppose that a subroutine gets called several times and that in the subroutine you need to use some computational result that stays the same for every call. You could use a state variable or an our variable.

sub foo {
    # ...

    state $some_value //= ... some computation ...;

    # ...
}

Using the defined-or operator, the value will only be computed the first time the subroutine is called.

But what if the computation takes several statements? You could move it to its own subroutine and call it with a single statement, or you might resort to something like:

# NOT IDIOMATIC
sub foo {
    # ...

    state $some_value;
    unless (defined $some_value) {
        # ... some more ...
        # ... intensive ...
        # ... computation ...
        $some_value = ...;
    }

    # ...
}

The problem is that we need to write $some_value three times.

But a more idiomatic way is to use an anonymous subroutine and call it in-place:

sub foo {
    # ...

    state $some_value //= sub {
        # ... some more ...
        # ... intensive ...
        # ... computation ...
    }->();

    # ...
}

About hanekomu

user-pic