Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Re: "foreach" is to "next" as "map" is to ???

by sacked (Hermit)
on May 26, 2004 at 16:27 UTC ( [id://356632]=note: print w/replies, xml ) Need Help??


in reply to "foreach" is to "next" as "map" is to ???

You can use the empty list () to prevent a return value from an interation of a map block:
my @nums= ( 0 .. 10 ); + my @odds= map { $_ % 2 ? $_ : () } @nums; + print "@odds"; __END__ 1 3 5 7 9

Here is (equivalent) code that uses foreach:
my @nums= ( 0 .. 10 ); + my @odds; + foreach( @nums ) { next unless $_ % 2; push @odds => $_; } + print "@odds";

Update: As others have stated, if you are not modifying the list, then you should use grep rather than map. Continuing with the code example from above, a better solution would be:
my @nums= ( 0 .. 10 ); + my @odds= grep { $_ % 2 } @nums; + print "@odds";

--sacked

Replies are listed 'Best First'.
Re: Re: "foreach" is to "next" as "map" is to ???
by Limbic~Region (Chancellor) on May 26, 2004 at 16:40 UTC
    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.

      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

      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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://356632]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-03-29 13:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found