coolmichael has asked for the wisdom of the Perl Monks concerning the following question:
I've been trying to wrap my head around map. I think I was going about it all wrong, trying examples and such just to see how it worked. It was probably to complicated for me, so I gave up and tried to play with grep for a while. It was a lot easier to understand. So now back with map, I've got a function that calculates a running total. My first thought was
map {$n[$#n+1]=($count+=$_)} <DATA>;
but then I realized that map was returning a list anyway, so I probably didn't need @n at all. I came up with
@n=map {$count+=$_} <DATA>;
Is this a good way to do it?
Also, I was wondering if you could do it with grep? I don't know why you'd want to, but it seems to me like you should be able to.
@n=map {$count+=$_} <DATA>;
print "@n\n";
__DATA__
1
2
3
6
-5
6
outputs this 1 3 6 12 7 13
which is what I was aiming for.
Re: playing with map
by archon (Monk) on Mar 09, 2001 at 13:15 UTC
|
map and grep are two different functions for a reason.
map basically says "take this list, do something to every element in it, and give me the resulting list." grep basically says "take this list, find all elements for which this expression is true, and give me the resulting list."
in general you don't want to use either of these functions in a void context, i.e. you wouldn't run them without assigning the list they return to some variable. also, you wouldn't want to use them unless you want to iterate through the entire list. in either of these situations, a foreach loop (with a call to last in the second situation) is more appropriate.
your realization and reconstruction of the map usage to assign the results of the operation to @n was correct. you couldn't do it with a grep call, because grep returns some subset of the original list. it doesn't make any modifications. also, as you said, why would you want to?
Update:Whoops.. guess i should have re-read the grep info before i made that last statement (= | [reply] |
|
> you couldn't do it with a grep call, because grep returns
> some subset of the original list. it doesn't make any
> modifications.
wrong. you could use grep, and it is destructive to the
input list. something like this works:
my @n = grep s/$_/$count+=$_;$_=$count/e, <DATA>;
i know it doesn't make any sense to use grep here, but
it can be done.
cheers
snowcrash ////// | [reply] [d/l] |
|
| [reply] |
|
| [reply] |
|
|
|
|
|
What tye said. Just because you can do something doesn't
mean that it isn't also a really stupid idea to do it. Using map or grep in void context is a sign of someone who has picked up bad habits.
UPDATE
I was asked why talking about modifying the input list
triggered comments about void context from me and tye.
Here is why. The typical bad idiom you see is to use a
grep in void context to
change the input list. Hence the alarm. Note that it is usually a bad idea to modify
the input list when not in void context, but occasionally
it may be natural to do that. For instance the input
list is temporary, and you want to both filter and modify
in an obvious way. Here is an example of a case where it
would fit:
return grep s/^FOR_PRINT://, <FILE>;
In my experience these cases tend to be rare.
UPDATE 2
merlyn is right. I would have to work harder to come
up with a place where modifying the input list makes sense. Given that I am not feeling well, I don't feel like doing that, and given that I think it is a bad idea, I don't think I should bother... | [reply] [d/l] |
|
Re: playing with map
by jeroenes (Priest) on Mar 09, 2001 at 13:30 UTC
|
Well, your map-code seems perfectly OK to me. No problems
there, unless you have non-numeric data. You could add
a test for that.
Your code can easily be written in grep, but it's less
efficient, as grep just is slower than map.
Maybe you can increase your understanding of map and
grep by looking at them as filters. You have a list,
put them in map, and you return something different.
In this respect grep is more like a seeve. In a diagram:
____________
<list> ---> | filter-code| --> <modified list>
------------
Than you flip the picture around, and you have a pretty
cood idea of what map does. It only reads right-to-left.
If you get this, you can start playing around with it.
For example, take multi-column data, split them, and count
the number of ones in the 3th column in a running fashion.
@n = map{ split;
$count += $_[2] =~ tr/1/1/;
print join "\t", $idx++, $_[2], $count, $count/ $idx;
print "\n";
$count;
}
<DATA>;
__DATA__
1 2 31 4
100 5 91111111111111111 1
3 4 -6 1
8888888 8 191 1
just to mention some funny useless stuff.
I hope this helps to see the power of map.
The next level than is the Schwartzian transformation.
Have fun with it,
Jeroen
"We are not alone"(FZ) | [reply] [d/l] [select] |
Re: playing with map
by Anonymous Monk on Mar 09, 2001 at 23:16 UTC
|
I have written lots and lots of Perl code without ever
really using the map command very much. But I often see
it in other's code. So, my impression is that the code
@new = map { act_on($_); } @old;
is functionally equivalent to
for (0..$#old) { $new[$_] = act_on( $old[$_] ); }
Is this a correct view of things?
CJW
| [reply] [d/l] [select] |
|
No, that presumes a 1-1 mapping. Some are, some aren't. It's more like this:
@new = ();
for (@old) {
push @new, act_on($_);
}
Consider this to see the difference:
@old = (10..20);
sub act_on {
return 1..$_;
}
The first element becomes 10 elements of output, the second becomes 11, and so on.
-- Randal L. Schwartz, Perl hacker | [reply] [d/l] [select] |
|
Not exactly, more like:
for (0..$#old) { push @new, act_on( $old[$_] ); }
usually written as:
foreach @old { push @new, act_on( $_); }
If act_on returns a list then all members are pushed into @old.
Update: darn! merlyn is right, you need to
initialize @new to () to get the exact equivalent statement.
| [reply] [d/l] [select] |
|
| [reply] |
|
|
|