in reply to Common Perl Idioms

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


Replies are listed 'Best First'.
Re^2: Common Perl Idioms
by ysth (Canon) on Jul 23, 2004 at 19:59 UTC
    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


        Ah, but !! is an idiom, and more readily recognizable to my eyes. With 0< it takes me a second to figure out what the code is trying to compare. In any case, 0 < has to load a constant then do a compare, so it's still two ops.
Re^2: Common Perl Idioms
by ihb (Deacon) on Jul 24, 2004 at 00:45 UTC

    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


        sub firstIdx { $_[ $_ ] eq $_[ 0 ] and return --$_ for 1 .. @_; return; }

        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algoritm, algorithm on the code side." - tachyon