in reply to Re: "foreach" is to "next" as "map" is to ???
in thread "foreach" is to "next" as "map" is to ???

sacked,
The empty list trick, while very cool, is not equivalent to next. Your equivalent for code should look like the following instead:
my @odd; for ( 0 .. 10 ) { push @odd unless $_ % 2; }
The reason I say this is that a map block is like a modifed subroutine that is applied to each item successively in the input list. Like regular subroutines, the last line of evaluated code is the return value. Unlike regular subroutines, you can not force it to return early by using return.

The empty list trick is just preventing the map block from returning a value for that input item - it still has to execut every step in the block first. In the case of next in a for loop, it actually jumps to the next iteration without executing the body of the block.

Cheers - L~R

Update: While I maintain that your example is not equivalent, it is possible to get the same effect. Placing a conditional where the two branches are an empty list and a block of code at every place you mean next gets the job done.

Replies are listed 'Best First'.
Re: "foreach" is to "next" as "map" is to ???
by Abigail-II (Bishop) on May 26, 2004 at 16:49 UTC
    The empty list trick, while very cool, is not equivalent to next.
    So, there is a difference? Let's see what that should be.
    The empty list trick is just preventing the map block from returning a value for that input item - it still has to execut every step in the block first.
    Really? Let's see, we take a list of numbers, and for all odd numbers, we print the square of the number, and return the number. The even numbers are skipped. According to your theory, the following code would print the squares of all the numbers:
    my @odds = map {$_ % 2 ? do {print $_ * $_; $_} : ()} 1 .. 10;

    However, if I run it, it only prints the numbers 1, 9, 25, 49 and 81. I guess either my perl is broken, or your theory is false.

    Abigail

      Abigail,
      If you use a ternary where it branches to the empty list or a do block for each place there is a next statement, then I will buy it doing the same thing.
      # contrived example my @paragraph; for ( @lines ) { next if /^#/; $_ = lc $_; $_ =~ tr/ -~//cd; next if /foobar/; $_ =~ s/administration/admin/g; next if length $_ > 60; push @paragraph, $_; }
      This IMO would be insane for a map block. I stand corrected on the ternary/empty list/do block being functionally equivalent though in sacked's original example there was no do block.

      Cheers - L~R

        though in sacked's original example there was no do block.
        Well, of course there wasn't a do block, because he was showing a foreach loop, and was asking how to do the equivalent using map. You don't need to do {} in the foreach.

        But sacked's original example didn't contain multiple nexts. There's also another way of dealing with this problem: a bare block:

        my @paragraph = map {;{ next if /^#/; $_ = lc $_; $_ =~ tr/ -~//cd; next if /foobar/; $_ =~ s/administration/admin/g; next if length $_ > 60; $_ }} @lines;

        Abigail

        I abhor foreach/push loops. Gag me with a spoon!

        my @paragraph = map { local $_ = lc; tr/-~//cd; /foobar/ ? () : do { s/administrator/admin/g; length() > 60 ? () : $_ } } grep /^#/, @lines;

        That code was ugly and I wouldn't have actually written that.

        my @paragraph = map { if ( /^#/ ) { () } else { local $_ = lc; tr/-~//cd; if ( /foobar/ ) { (); } else { s/administration/admin/g; length() > 60 ? () : $_; } } /^#/ ? do { } : (); } @lines;
Re: Re: Re: "foreach" is to "next" as "map" is to ???
by sacked (Hermit) on May 26, 2004 at 16:45 UTC
    I meant "equivalent" in the sense that @odds is the same after both the map and the foreach.

    As far as equivalent code, I'd argue that this is closer to the map than your for loop using unless:
    my @odds; for ( 0 .. 10 ) { push @odds => $_ % 2 ? $_ : (); }
    In the same way that the original foreach using next does not evaluate the push for every iteration, your code using push/unless does not evaluate the push every iteration.

    --sacked
      sacked,
      Maybe I am just nit picking, but if you meant equivalent results you should not have said equivalent code.
      for ( @lines ) { next if /^#/; # ... a dozen lines of code push @paragraph, $_; }
      There is no sane way to do the equivalent of this in a map block. You can only choose not to provide the line to the paragraph at the last instruction.

      L~R

        There is no sane way to do the equivalent of this in a map block.
        Of course there is.
        map { !/^#/ ? () : do { # ... a dozen lines of code } } @lines

        Abigail

        You wrote:
        There is no sane way to do the equivalent of this in a map block.
        @paragraph= map { if( /^#/ ) { () } else { # ... a dozen lines of code $_ } } @lines;

        --sacked