throop has asked for the wisdom of the Perl Monks concerning the following question:

Looking over some code I saw
my $text; for $text(@array){ $hash->{$text}++}
"That won't work," I thought. "It should be:"
my $text; foreach $text(@array){ $hash->{$text}++}
But investigation showed that it worked fine. Further,
my @foo = qw(bar baz ne); foreach (my $ix=0;$ix < @foo; $ix++){print $foo[$ix]}
printed out barbazne.

So it looks like for and foreach will accept each others' formats. Are they in fact equivalent? Or is code that exploits this vulnerable to subtle bugs?

thanks
throop

Replies are listed 'Best First'.
Re: Risks of equivalence between FOR and FOREACH
by ikegami (Patriarch) on Oct 31, 2006 at 22:09 UTC
    I know of four kinds of for/foreach loops in Perl. In all cases, for and foreach are interchangeable. The interchangeability is documented in perlsyn.
    • Iterating loop

      foreach my $i (...list...) { ... }

      The list is flattened (requiring memory proportional to the size of the list).

    • C-style loop

      for (...; ...; ...) { ... }

      Utmost flexibility. Utmost efficiency. Least readable.

    • Counting loop

      for my $i ($x..$y) { ... }

      This is a special case of the iterating loop that uses no memory. When the list consists of a range, it is not flattened. Utmost efficiency. Utmost Readability.

    • Reverse Iterating loop

      foreach my $i (reverse ...list...) { ... }

      This is a special case of the iterating loop. Instead of actually reversing the list, the list is processed in reverse. You save the cost of the reverse operation, but that's it. The list is still flattened (even if it's just a range), so it still requires memory proportional to the size of the list.

    My personal convention is to write counting loops and C-style loops using for, and iterating loops using foreach. That way, the extra cost of flattening the list is not hidden. However, Perl will gladly accept both for and foreach in every case.

    Update: Since posting this, I have been told Reverse Iterating loop has been improved such that it no longer flattens the list, but the following snippet shows that it hasn't been "fixed" in the latest stable version of Perl (Perl 5.8.8).

    use strict; use warnings; print("$]\n"); # 5.008008 # Don't inline these. It will cause the # memory to be allocated at compile time. my $min = 0; my $max = 10_000_000; for ( $min .. $max) { print(":"); <STDIN>; last; } # 2.2MB for (reverse $min .. $max) { print(":"); <STDIN>; last; } # 239MB

    The person must have been misinterpreted perl586delta. perl586delta only says "a temporary reversed list isn't created". The temporary flattened list is still created.

      My personal convention is to write counting loops and C-style loops using for, and iterating loops using foreach. That way, the extra cost of flattening the list is not hidden. However, Perl will gladly accept both for and foreach in every case.

      And many people share your pov, while I'm of the "three chars are easier to type no matter what" school of thought - I also think we've "argued" about these issues in the past, but I'm not really sure. However to contribute with something concrete to this thread, it's also interesting if not properly relevant, to note that in Perl 6 the possible/alleged ambiguity will be removed, with C-style for loops being renamed to loop, and for being the preferred kind of loop for most situations, while foreach will be phased out:

      pugs> sub postfix:<!> (Int $x) { [*] 1..$x } undef pugs> say $_! for =<>; 3 6 4 24 7 5040 10 3628800 undef pugs>
Re: Risks of equivalence between FOR and FOREACH
by jeffa (Bishop) on Oct 31, 2006 at 21:54 UTC

    Try this on the command line and see what Perl does for yourself. :)

    perl -MO=Deparse -e "print for 1..10"

    UPDATE: By the way, if you had simply typed FOR and FOREACH into the simple search box (found at the top of each page) you would have stumbled across this node: For and foreach....

    I should also add that my personal preference is to only use FOR. I see no reason to type the extra characters, no matter what kind of loop i am using. I do not agree that FOREACH is more readable than FOR. (You could always say EACH to yourself when reading the code. ;))

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Risks of equivalence between FOR and FOREACH
by imp (Priest) on Oct 31, 2006 at 21:55 UTC
    From the perlsyn documentation:
    The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity.
Re: Risks of equivalence between FOR and FOREACH
by Fletch (Bishop) on Oct 31, 2006 at 21:55 UTC

    Or just RTFM.

    The "foreach" keyword is actually a synonym for the "for" keyword, so you can use "foreach" for readability or "for" for brevity. (Or because the Bourne shell is more familiar to you than csh, so writing "for" comes more naturally.) If VAR is omitted, $_ is set to each value.