Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

map in void context

by dharanivasan (Scribe)
on Dec 17, 2008 at 11:58 UTC ( [id://730903]=perlquestion: print w/replies, xml ) Need Help??

dharanivasan has asked for the wisdom of the Perl Monks concerning the following question:

Is it good practice to use map in void context ?.
use strict; use warnings; my $variable = "Hello World"; ucfirst($variable); print $variable;
I checked perl syntax using perl -cw . It warn as follows:
Useless use of ucfirst in void context at uc.pl line 29.
uc.pl syntax OK
But following code doesn't warn anything , it seem map can be used in void context ..
map { $sum += $index{$_}} split (//,$word);
Correct me if I am wrong..

Replies are listed 'Best First'.
Re: map in void context
by almut (Canon) on Dec 17, 2008 at 13:04 UTC

    The essential difference between ucfirst and map is (with respect to void context), that ucfrist does nothing but return something, while the code executed via map may have side effects. Thus, the former is clearly 'useless', while the latter could be potentially 'useful'.  Still, it usually isn't considered good practice to use map in void context (use for instead, e.g. $sum += $index{$_} for split (//,$word);).

Re: map in void context
by kyle (Abbot) on Dec 17, 2008 at 13:17 UTC

    is the use of map in a void context deprecated ?

    I don't like map in void context, personally. There's for if you just want a loop. I think of map as returning a list, and if you're discarding the list, I wonder if there's something wrong. I think, "why didn't the programmer just use for?"

      I think of map as returning a list, and if you're discarding the list, I wonder if there's something wrong. I think, "why didn't the programmer just use for?"
      But that shows more about you than about the programmer. Nothing in void context (or even non-void scalar context) returns a list.

      And the efficiency bug that made map construct a list internally was solved many years ago. As for more than one way to make a list, the programmer doesn't just have the option between map and for, there's also while, until, goto and bare blocks. And C-style for. I never think seeing one loop "Hmm, the programmer didn't use any of the other 6 types of loops, I wonder what's wrong". That only hinders my ability to understand the code.

        But that shows more about you than...

        That's part of why I used the word "personally".

        In the code at $work right now, I find this (variable names changed to protect the guilty):

        map { push @result, $_ if $_ } @{ $obj->method };

        Why was this not written as...

        push @result, grep { $_ } @{ $obj->method };

        In fact, why would I ever have push as the only thing inside a map (a pattern I see repeated throughout code written by this programmer)? Maybe there used to be more code there that made that make sense?

        Code should have some semantic meaning beyond the nuts and bolts of what it does. To me, map means "transform one list into another list". It does that using a loop, and so it can be used more generally for looping (like the other constructs you mention), but it has a specialty. I'd say the same thing about grep, which, in void context, is identical to map.

Re: map in void context
by zentara (Archbishop) on Dec 17, 2008 at 12:30 UTC
    Void context is discussed in many nodes here, like Unhappy returns . Do a 'perldoc -q void", or you can google for "Perl void context". Basically it's pretty complex depending on function and usage, and unless you want to really clutter your mind, just deal with it when you get the warning and give the function a place to put it's return value(s). Read the perldoc for the functions.... ucfirst says it returns a value, map evaluates.

    I'm not really a human, but I play one on earth Remember How Lucky You Are
Re: map in void context
by Bloodnok (Vicar) on Dec 17, 2008 at 12:06 UTC
    See ucfirst - it [ucfirst()] returns the changed string and you're ignoring it - hence the warning;

    A user level that continues to overstate my experience :-))
Re: map in void context
by ikegami (Patriarch) on Dec 17, 2008 at 14:11 UTC

    Side-effects are something to be avoided when programming. I also like my code to be as self-documenting as possible. I find map is less than optimal for both of these reasons. I use for as my topicalizer.

    $sum += $index{$_} for split(//, $word);
      Side-effects are something to be avoided when programming.
      In Perl? So, you'd prefer to avoid assignment, print statements, regular expressions, and even fetching the value of a variable?

      There are programming languages that are more suitable for avoiding side-effects. But programming in Perl while avoiding side-effects makes as much sense as writing English novels while avoiding vowels.

      I also like my code to be as self-documenting as possible.
      Who doesn't? For me having code as much self-documenting as possible doesn't mean map in void context isn't.
      I find map is less than optimal for both of these reasons.
      $sum += $index{$_} for split(//, $word);
      The map-in-void context equivalent would be
      map $sum += $index{$_}, split(//, $word);
      I see as many side-effects as the code using 'for', so your example doesn't prove your first reason. In fact, the 'side-effect' reason people often come up with doesn't make any sense. If you do map EXPR, LIST or map BLOCK LIST, the side-effects are in EXPR or BLOCK. The 'for' equivalents are EXPR for LIST and for (LIST) BLOCK. Same EXPR and BLOCK. Same side-effects.

      As for documentation (or clearness) issues, I see differences between:

      map {BLOCK} LIST
      and
      for (LIST) {BLOCK}
      and that's the order of BLOCK and LIST. If I find the action more important, I prefer map {BLOCK} LIST, as it lists the statement(s) first. If I want to put the focus on the data, I use for (LIST) {BLOCK}. (There's also a difference in context, map gives list context, for void context, but that's seldomly an issue).

      I hardly ever use the map EXPR, LIST construct though (regardless of context), as it doesn't clearly separate the action from the data.

        I don't consider assignment to have a "side effect". Assignment is the effect. It might have a side effect if I'm working with a tied variable, I guess, but that's out somewhere else.

        Another difference between for and map is what happens when the BLOCK modifies the array that it's iterating over.

        my @x; # loops once @x = qw( x ); map { push @x, 'x' } @x; # loops forever @x = qw( x ); push @x, 'x' for @x;

        So, you'd prefer to avoid assignment,

        I don't know what side effects you are talking about for assignments.

        print statements,

        I'm not fond of how print changes $!, but it's much more convenient than the alternative. There's also a very high level of consistency with other like functions. system is where it goes overboard ($?).

        regular expressions,

        I don't use $1, etc. I use the match operator in list context instead.

        and even fetching the value of a variable?

        Memoizing is a harmless side-effect.

        I see as many side-effects as the code using 'for'

        As I see it,
        The purpose of for is to affect something (necessarily external) repeatedly.
        The purpose of map is to transform a list.
        You may see things differently, but it was clear that I was explaining how I see things.

        A reply falls below the community's threshold of quality. You may see it by logging in.
Re: map in void context
by shmem (Chancellor) on Dec 17, 2008 at 18:35 UTC

    Others have already said that map returns a list, which is discarded in void context. It works like for and, like for, operates on an alias ($_) of each element of the list passed, so you can use it to edit list elements in-place.

    But it constructs a list of results, and that list consumes memory:

    perl -le '@l=0..2**16;$_++for@l; print `ps -o vsz= -p $$`*1024;print f +or @l[0..3]' 10723328 1 2 3 4 perl -le '@l=0..2**16;map{$_++}@l; print `ps -o vsz= -p $$`*1024;print + for @l[0..3]' 11771904 1 2 3 4

    Also, the map variant is two chars longer than the for one, so you won't see Golfers using it in void context...

      Not that I've checked, but I recall a rumour that modern-enough perls (5.8.8, maybe) detect map in a void context and drop the list creation to save time and space. I don't see a point: the definition of map is "Evaluates ... and returns the list" whereas foreach (aka for) "is an iterator: it executes the statement once for each item". So map should return a list (it has explicit documentation regarding scalar context), and for shouldn't really have a return (or an accumulator).

      And the map variant is actually only one char longer if you golf it right:

      $_++for@l map$_++,@l
      But I still prefer map for mapping, and for for iterating.

Re: map in void context
by JavaFan (Canon) on Dec 17, 2008 at 13:31 UTC
    Is it good practice to use map in void context?
    That depends. Does it make you feel uncomfortable? Then it's bad practice. Do you like it? Then it's good practice.

    One of the reasons we love Perl is that it allows a range of coding styles to pick from. It's good practice to use a style that suits you and not someone else on an internet forum.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2024-04-25 23:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found