Re: "foreach" is to "next" as "map" is to ???
by sacked (Hermit) on May 26, 2004 at 16:27 UTC
|
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";
| [reply] [d/l] [select] |
|
|
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. | [reply] [d/l] |
|
|
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 | [reply] [d/l] |
|
|
|
|
|
|
|
|
|
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.
| [reply] [d/l] [select] |
|
|
|
|
|
|
Re: "foreach" is to "next" as "map" is to ???
by hardburn (Abbot) on May 26, 2004 at 16:20 UTC
|
| [reply] |
|
|
grep { TEST }
map { TEST ? $_ : () }
| [reply] [d/l] |
|
|
grep is just a specialized form of map
Not entirely true.
The difference between grep { TEST } and
map { TEST ? $_ : () } is that grep
returns a list of aliases to the elements that
match TEST. map doesn't do this for obvious reasons -
its return can be anything, it's in no way bound to $_.
For example, if you have an array of numbers and you want to set any negative
elements to zero, you can do it with the following code (of course this
is not the most common way):
$ perl
@ary = (1, -5, -12, 7, 3);
$_ = 0 for grep {$_ < 0} @ary;
print "@ary\n";
^D
1 0 0 7 3
| [reply] [d/l] [select] |
Re: "foreach" is to "next" as "map" is to ???
by Belgarion (Chaplain) on May 26, 2004 at 16:22 UTC
|
Hmm, I don't think you can exclude something from inside a map, but you could place a grep before the map call. Basically, the grep would remove anything you didn't want before it's passed to the map function. So, you would have something like:
@array = map { # do stuff }
grep { m/good stuff/ } @array;
This would pass anything item that matched "good stuff" to the map function for further processing.
Update After reading through the other comments posted, I guess I should not be surprised that map is more powerful than I had initially expected. I, personally, happen to think the map/grep combo is clearer code in this example, but the additional power of map is certainly nice to know.
| [reply] [d/l] |
Re: "foreach" is to "next" as "map" is to ???
by Abigail-II (Bishop) on May 26, 2004 at 16:40 UTC
|
map {m ["good stuff"] ? do { DO STUFF } : ()} @array;
Abigail | [reply] [d/l] |
Re: "foreach" is to "next" as "map" is to ???
by blokhead (Monsignor) on May 26, 2004 at 16:29 UTC
|
Remember, you can return a list of any size from map's block:
my @results = map { m/good stuff/ ? do { ... } : () } @array;
So return an empty list to produce a no-op in map's output. If you don't want the whole thing wrapped in a conditional like this, you can also use return () somewhere in the block as a guard condition in place of next, but you have to mess with the syntax a little:
my @results = map sub { return () unless m/good stuff/; ... }, @array;
Note the sub keyword, and the comma after the sub. All in all, it's probably clearest to tack on a grep before the map:
my @results = map { ... } grep { m/good stuff/ } @array;
| [reply] [d/l] [select] |
Re: "foreach" is to "next" as "map" is to ???
by BrowserUk (Patriarch) on May 26, 2004 at 18:42 UTC
|
Reading between the lines (or rather between the nodes) and assuming that you are trying to combine the two loops in this node, then the empty list technique is the most applicable.
my @sorted = map{
substr $_, 5;
}sort {
$b cmp $a
} map{
m[,\s+(\d+)]
? sprintf '%05d%s', $1, $_
: ()
} @msgs;
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [d/l] |
|
|
Wow. You make that look so *easy*... =)
Thanks for jumping in. I've been breaking apart the GRT into little sections, asking questions about various parts. I guess you figured that out.
There's still lots of stuff here for me to study, but this is definitely what I was trying to get to.
| [reply] |
|
|
It's Perl, it's original design and many years of refinement that make this sort of thing easy. A language developed to solve problems rather than comply to some academic or commiteed theory.
It just takes a while to encounter all the ways to use it, and a bit longer to be able to remember them all. I'm still a relative novice and still learn something new here almost every day.
Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
| [reply] [d/l] |
|
|
You make that look so *easy*...
Think about map, grep, and sort as operating on lists as a whole. That's what made the functional parts of Perl really click for me. for-loops operate on items that just happen to be bundled in lists. map and grep operate on lists that just happen to have items in them.
------
We are the carpenters and bricklayers of the Information Age.
Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose
I shouldn't have to say this, but any code, unless otherwise stated, is untested
| [reply] |
|
|
|
|
Re: "foreach" is to "next" as "map" is to ???
by Enlil (Parson) on May 26, 2004 at 16:28 UTC
|
grep is a good way to do it another way would be: @modified_list = map {
$_ =~ /good stuff/ :
#do stuff here
? () } @array;
or the map grep combo:@modified_list = map {
#do stuff here
} grep { /good_stuff/ } @array
-enlil | [reply] [d/l] [select] |
Re: "foreach" is to "next" as "map" is to ???
by injunjoel (Priest) on May 26, 2004 at 16:24 UTC
|
Greetings all,
Isn't this what grep is for?
As I understood it map will #DO STUFF for each element of @array and copy the results to whatever called map{}.
If you don't want a copy for every element in @array use grep{} which will only copy the results based on some condition eg: next "Bogus" unless m["good stuff"]
Of course this is to the best of my understanding...
-injunjoel
| [reply] [d/l] [select] |
Re: "foreach" is to "next" as "map" is to ??? (why obfu?)
by tye (Sage) on May 26, 2004 at 17:41 UTC
|
It looks like you aren't using the returned list from map. That is one good reason to use foreach instead. Now you also want to use next. From what you've shown, I think the best solution is to use foreach (unless you are going for obfuscation). (:
| [reply] |
Re: "foreach" is to "next" as "map" is to ???
by dimar (Curate) on May 26, 2004 at 17:49 UTC
|
### INIT pragma
use strict;
use warnings;
### PROCESS
my @array = <DATA>;
map { (0)?()
: (/good stuff/) ? do{print "DO STUFF\n";}
: (/[^a-z]+/) ? do{print "BAD STUFF: -> $_";}
: do{} ### do nothing
} @array;
### SAMPLE output
q^*******************q
DO STUFF
BAD STUFF: -> garbage
DO STUFF
BAD STUFF: -> unknown
BAD STUFF: ->
BAD STUFF: -> the previous line is blank
DO STUFF
DO STUFF
q********************q^if(0);
__DATA__
good stuff
garbage
good stuff
unknown
the previous line is blank
good stuff
good stuff
| [reply] [d/l] |