Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Common Perl Idioms

by eric256 (Parson)
on Jul 23, 2004 at 18:04 UTC ( [id://376948]=perlmeditation: print w/replies, xml ) Need Help??

In response to a recent post about Perl Idioms I decided to start a list of perl Idiom. Below are the few I have compiled so far. Reply or msg me with more and I will add them to the main body here. To start us off:

File Slurping

# Tye + Juerd in id=204874 sub slurp { local( *ARGV, $/ ); @ARGV = shift; <> }; # slurp a bunch of files @files = do { local(@ARGV, $/) = @filenames; <> };
Array/List Stuff
# get the indexes of items in an array @foo{ @foo } = 0 .. $#foo; $foo{'abc'} # the index of 'abc' in @foo # dragonchild id=376694 sub unique { my %x; grep { !$x{$_}++ } @_ } # broquaint id=374287 sub in_list {return !!grep { $_ eq $_[0] } @_[ 1 .. $#_ ];} # ysth sub in_list {my $target=shift; return !!grep { $_ eq $target } @_} # demerphq sub find_first_index { my $target=shift; push @_,$target; my $i=0; $i++ while $_[$i] ne $target; return $i==$#_ ? undef : $i }

___________
Eric Hodges

Replies are listed 'Best First'.
Re: Common Perl Idioms
by Eimi Metamorphoumai (Deacon) on Jul 23, 2004 at 18:20 UTC
    Wouldn't in_list be faster if it broke out early, instead of comparing all the items and then coercing to boolean? Something like
    sub in_list { ( $_ eq $_[0] ) && return 1 for @_[ 1 .. $#_ ]; undef;}
    Though personally I'd go with the less compact, but more readable
    sub in_list { for (@_[ 1 .. $#_ ]){ return 1 if $_ eq $_[0]; } undef; }
    Since if it's going to be a sub anyway, you might as well make it a little easier to read.
Re: Common Perl Idioms
by demerphq (Chancellor) on Jul 23, 2004 at 19:44 UTC

    Regarding in_list I personally would prefer either:

    sub in_list {my $target=shift; return 0+grep { $_ eq $target } @_}

    if you really care about the count (and want to use grep) or if you want fast then:

    sub in_list {my $target=shift; $_ eq $target and return 1 for @_; retu +rn}

    using the for modifier avoids creating and managing an extra scope (like grep,map and for statements) and avoiding the slice I think is more efficient. Afaik perl will have to create the sliced list first before iterating so you are really just copying the whole array which could get expensive for a large list.

    Anyway heres an interesting/useful one :

    sub find_first_index { my $target=shift; push @_,$target; my $i=0; $i++ while $_[$i] ne $target; return $i==$#_ ? undef : $i }

    Which is a nice trick to reduce the cost of the tight loop. Dunno if more modern perls have been optimised enough that these tricks are unnecessary though. Its been a while since I benchmarked this stuff last.


    ---
    demerphq

      First they ignore you, then they laugh at you, then they fight you, then you win.
      -- Gandhi


      I would have said !!grep, not 0+grep. 0+ will return 0 or 1. !! will return canonical true or false.

        Well I'm not so sure how important that is, especially as it loses useful and already obtained information (ie the count). Also for the canonical return isn't 0 < grep better? It would also return the canonical true/false but is only a single operator...


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


      An alternative to your &find_first_index that I prefer (for smaller lists) is

      use List::Util 'first'; sub find_first_index { my $target = shift; first { $_[$_] eq $target } 0 .. $#_; } # Simple enough to write inline: my $idx = first { $ary[$_] eq $target } 0 .. $#ary;
      I don't know about any efficiency issues though.

      Update: Fixed a off-by-one bug in the sub and added parenthesis.

      ihb

      Read argumentation in its context!

        I don't know about any efficiency issues though.

        Doesn't look good:

        Rate ihb_zip ihb_hit dmq_zip dmq_hit ihb_zip 8461/s -- -5% -44% -50% ihb_hit 8869/s 5% -- -42% -47% dmq_zip 15177/s 79% 71% -- -10% dmq_hit 16782/s 98% 89% 11% --

        And thats using the XS List::Util implementation from 5.8.2. Theres a lot of overhead in calling the closure, and I think that the range operator isn't lazy in this usage, so it has to manufacture a list of indexes to use.


        ---
        demerphq

          First they ignore you, then they laugh at you, then they fight you, then you win.
          -- Gandhi


Re: Common Perl Idioms
by atcroft (Abbot) on Jul 23, 2004 at 20:13 UTC

    I don't know if this is up to the level of some of the other code here, but sometimes, when I want to verify the contents of a few variables (here $var1, @var2, and %var3, for example purposes), I will toss the following into the code:

    if (1) { use Data::Dumper; local($| = 1); print Data::Dumper->Dump([\$var1, \@var2, \%var3], [qw(*variable1 *variable2 *variable3)]), "\n"; }

    I can then go back and change it to if (0) to turn it off (and leave it there if I expect to need to look at the content of the variables at some point in the future), or remove it entirely once I'm finished.

    Don't know if it helps anyone else, but it has proven helpful to me in the past. (And my heartfelt thanks to the monks who led me to using it, after I first started visiting.)

      Did you know that the 'if(0)' won't inhibit the loading of Data::Dumper, even if the 'use' is inside the 'if'? That's because 'use' operates at compile time before the optimizer has a chance to remove the dead code.

      -sam

        I learnt this the hard way. I used to write:

        if ( $DEBUG ){ use CGI::Carp qw(fatalsToBrowser); }

        thinking that when I put the code into production all my debugging would dissapear with a simple $DEBUG=0; but Carp is always loaded no matter the value of $DEBUG. It took a long while for me to notice the error becuase when run in prod the conditions which would trigger a die didn't normally occur. My bad.

        --
        Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

      What's particularly good with if (FALSE_CONSTANT) { ... } is that the whole block will be optimized away during the compilation phase.

      ihb

      Read argumentation in its context!

Re: Common Perl Idioms
by Anonymous Monk on Jul 23, 2004 at 19:20 UTC

    Some of the ones I can remember off the top of my head...

    # slurp a bunch of files @files = { local(@ARGV, $/) = @filenames; <> }; # get the indexes of items in an array @foo{ @foo } = 0 .. @foo; $foo{'abc'} # the index of 'abc' in @foo # Get the sign of a number $a <=> 0 # test primality sub isPrime { (1 x $_[0]) !~ /^(11+)\1+$/ } # (ok I never use that in production code # but it is funny)

    Ted

      Your slurp:

      @files = { local(@ARGV, $/) = @filenames; <> }; # missing 'do'

      Should actually be:

      @files = do { local(@ARGV, $/) = @filenames; <> };

      And your index hash:

      @foo{ @foo } = 0 .. @foo; # off-by-one error

      Should be:

      @foo{ @foo } = 0 .. $#foo;

        You are right about the slurp, a typo on my part.

        But the indexing will work. The extra number (int @foo) will just be ignored because there won't be a corresponding lvalue.

        Ted :->
      1 is not prime. Neither is 0.
      # Get the sign of a number
      $a <=> 0

      Or just use "abs".

      Ignore me. I'm talking nonsense.

      --
      <http://www.dave.org.uk>

      "The first rule of Perl club is you do not talk about Perl club."
      -- Chip Salzenberg

        Or just use "abs".

        Eh?

      The @foo{ @foo } = 0 .. @foo; seems very clever to me (not to mention useful), but being a bit of a newbie, I don't really understand how it works. Anyone care to enlighten me?
        It's creating a hash named %foo using a hash slice built out of the elements in @foo. Then it's assigning to that slice the values 0 to (the number of elements). So if @foo = ('a', 'b', 'c'), you get
        @foo{'a', 'b', 'c'} = (0, 1, 2);
        which is itself just shorthand for
        $foo{'a'} = 0; $foo{'b'} = 1; $foo{'c'} = 2;
        (technically, @foo in scalar context returns a value one too large, so we're really mapping to (0,1,2,3), but the odd element gets ignored. It might be more proper to use $#foo there (the index of the last element, instead of the number of elements))
        You want to look up hashslices. That's going to be the key.

        ------
        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

Re: Common Perl Idioms
by ysth (Canon) on Jul 30, 2004 at 08:43 UTC
    theorbtwo reminded me of this one:
    # build a regex matching any of an array of strings $re = qr/@{[join "|", map { quotemeta $_ } sort { length $b <=> length + $a } @strings]}/;

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://376948]
Approved by dragonchild
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-28 18:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found