SPUG: Readable, well-written code (was: Best One-Liners and Scripts for UNIX)

Jeremy Kahn kahn at cpan.org
Mon Apr 21 00:56:36 CDT 2003


Richard Anderson wrote:

>This use of grep exploits a lesser-known side effect of the function and
>bypasses it's main purpose, which is to select elements of a list.  If you
>want to apply a transform to every element of a list, Perl provides the map
>function:
>
>map { $_ =~ s/(\d+)/$1 * 2/e } @list;
>  
>
First of all, the $_ =~ is unnecessary.  This could be written:

  map { s/(\d+)/$1*2/e } @list;

Second, both of these uses of 'map' are confusing and not-recommended, 
since they use map in a void context. (Brian's [ab]use of 'grep' also 
sins.)  See PerlFAQ 6, (`perldoc -q "map in a void"`) which reads:

> Found in /usr/lib/perl5/5.8.0/pod/perlfaq6.pod
>        What's wrong with using grep or map in a void context?
>
>                The problem is that both grep and map build a return list,
>                regardless of the context.  This means you're making 
> Perl go to
>                the trouble of building a list that you then just throw 
> away.
>                If the list is large, you waste both time and space.  
> If your
>                intent is to iterate over the list then use a for loop 
> for this
>                purpose.


So, rewrite again as:

  foreach (@list) { s/(\d+)/$1*2/e };

>or, preserving the input @list:
>
>@doubled = map { ($x = $_) =~ s/(\d+)/$1 * 2/e;
>                               $x;
>                            } @list;
>
Yikes. I have to agree with Brian (and as a teacher) that phrases[1] like

   ($x = $_) =~ ...

are very very hard. Where does a novice even start to look that up?  At 
least it won't blow up my shell when I type "perldoc -f map". By 
contrast, have you ever tried `perldoc -f $_` in a bash shell? Do it 
*only* immediately after doing something safe, like "echo foo".  Caveat 
user...

If preserving the input list is critical, I recommend the KISS approach:

  @doubled = @list; # keep original @list unchanged
  foreach (@doubled) {
    s/(\d+)/$1 * 2/e; # double the first set of digits.
  }

Just my $0.02.

--jeremy

[1] I am using the Perl 6 operator '...', pronounced "yadda yadda 
yadda", in this example.




More information about the spug-list mailing list