Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

playing with map

by coolmichael (Deacon)
on Mar 09, 2001 at 13:05 UTC ( #63200=perlquestion: print w/ replies, xml ) Need Help??
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.

Comment on playing with map
Select or Download Code
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 (=

      > 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 //////
      Ahum, grep can be used to modify the list. From perlfunc (at my system, 5.6):

      Note that, because `$_' is a reference into the list value, it can be used to modify the elements of the array. While this is useful and supported, it can cause bizarre results if the LIST is not ...

      Jeroen
      "We are not alone"(FZ)

        Well, kinda. With grep, for each item you can only:

        • Leave the item alone and not include it in the result
        • Modify the original item and not include it in the result
        • Include the original item, unmodified, in the result
        • Modify the original item and include this exact same modification in the result
        With map you can do all of the above and also do:
        • Leave the item alone and include one or more values in place of that item in the result
        • Modify the item and include one or more values in place of that item in the result, with the ability to even include the original (unmodified) item in the result.
        So using grep to return something other than a subset of the original list is quite restricted and is probably an indication that you shouldn't be using grep. (:

                - tye (but my friends call me "Tye")
        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...

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)

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

      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

      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.

        If act_on returns a list then all members are pushed into @old.
        In a list context, act_on must return a list! And map (and the map-equivalents) are providing list context.

        -- Randal L. Schwartz, Perl hacker

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://63200]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2014-11-28 07:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My preferred Perl binaries come from:














    Results (193 votes), past polls