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

Hi, I need a sub that finds the first occurence of a string within an array and returns a new array containing all remaining elements after this first occurance.

Here is what I had thought to be an elegant solution:

sub blah { my($look_for, @array)=@_; my (undef, @elements_we_want) = grep { $_ eq $look_for .. 1 } @array; return @elements_we_want; }
And this acually work - the first time that is.

my @a = qw(hubba bubba abba zappa); blah("bubba", @a); # this gives qw(abba zappa) as desired
The problem is the global state that the f**ing flip-flop operator holds and that does not get reset on calling the sub the second time.

So my questions would be is there any elegant way to salvage the approach with the flip-flop or can someone recommend a better way of doing it?

Many thanks!

Replies are listed 'Best First'.
Re: flip-flop flop
by moritz (Cardinal) on Mar 30, 2012 at 10:03 UTC

      Because  firstidx returns -1 if the test block never evaluates true, a test is needed avoid returning all elements if the looked-for element is not found:
          my $from = firstidx {$_ eq $look_for} @array;
          return unless $from > -1;
          return @array[$from+1..$#array];

Re: flip-flop flop
by BrowserUk (Patriarch) on Mar 30, 2012 at 10:44 UTC

    sub after{ my $target=shift; shift @_ until $_[0] eq $target; return @ +_ };; @a = 'a'..'z';; print after( 'm', @a );; m n o p q r s t u v w x y z print after( 'e', @a );; e f g h i j k l m n o p q r s t u v w x y z print after( 'y', @a );; y z

    Add another shift if you don't want the element you looked for.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

      So far all approaches seem to avoid the flip-flop.

      It would be very curious to see if there is a way to reset the flip-flop in some sane way (you probably need to create a new closure to use in the grep every time you call it or something).

      This was one of the very few times I tried to make use of the flip-flop and more or less every time I try to use it I have to abandon it in the end.

      Because of this global-state issue the flip-flop is pretty much useless to me - to the point where I think perl would be better off without it...

        Because of this global-state issue the flip-flop is pretty much useless to me

        My take is that the flip-flop is very useful for one-liners were is compactness is invaluable, and I wouldn't want to lose that.

        But I cannot remember the last time I used in a script. What it does can always be done better -- though more verbosely -- when you have the space.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

        One way to "reset" the flip-flop is to recompile its statement.
        use strict; my @a = qw(hubba bubba abba zappa); print blah("bubba", @a), "\n"; print blah("bubba", @a), "\n"; sub blah { my($look_for, @array)=@_; my @elements_we_want; my $string = '(undef, @elements_we_want) = grep { $_ eq $look_for .. 1 } @a +rray;'; eval $string; return @elements_we_want; }
        Edit: Sorry, I did not notice the similiar idea by JavaFan.
Re: flip-flop flop
by kcott (Archbishop) on Mar 30, 2012 at 10:14 UTC

    Here's a technique you might find useful if you have Perl 5.10 or higher.

    $ perl -Mstrict -Mwarnings -E ' > my @x = (0 .. 10); > my @y = grep { state $z = 0; $_ == 3 and $z = 1; $z } @x; > say qq{@y}; > ' 3 4 5 6 7 8 9 10

    Update: I changed $_ > 3 to $_ == 3 to better illustrate the principle.

    Sorry. Crap answer - see JavaFan's comments (below) for the reason why. Only read the first half of the question. Here's a better answer, in the same vein.

    $ perl -Mstrict -Mwarnings -E ' > sub blah { my $z = 0; [ grep { /3/ and $z = 1; $z } @_ ] } > my @x = (0 .. 10); > say qq{@{blah(@x)}}; > my @y = qw{a b c 3 d e f}; > say qq{@{blah(@y)}}; > ' 3 4 5 6 7 8 9 10 3 d e f

    -- Ken

      Suffers from the same problem the OP describes. If you would use that grep a second time (because it's in a sub, or in a loop), the $z doesn't get reset -- that's what state is doing.

      Better (but untested):

      my @y = do {my $z = 0; grep {$z ||= $_ == 3} @x};
      Note that doesn't evaluate the condition ($_ == 3) after finding a match.

        ++ Thanks JavaFan, I've updated my post.

        -- Ken

Re: flip-flop flop (reset)
by tye (Sage) on Mar 30, 2012 at 14:08 UTC

    My first success at resetting the flip-flop:

    #!/usr/bin/perl -l sub after { my $skip = shift @_; my( undef, @want ) = grep { $_ eq $skip .. ref($_) && $_ == \$skip } @_, \$skip; pop @want; return @want; } print join ", ", after(qw< c a b c d e f g >); print join ", ", after(qw< e a b c d e f g >); print join ", ", after(qw< z a b c d e f g >); print join ", ", after(qw< f a b c d e f g >); print join ", ", after(qw< g a b c d e f g >); print join ", ", after(qw< a a b c d e f g >); __END__ d, e, f, g f, g g b, c, d, e, f, g

    Not that I'd find myself likely to actually use such a thing, even with my more elegant solution:

    #!/usr/bin/perl -l sub after { my $skip = shift @_; my( undef, @i ) = grep $_[$_] eq $skip .. $_ == $#_, 0..$#_; return @_[@i]; } print join ", ", after(qw< c a b c d e f g >); print join ", ", after(qw< e a b c d e f g >); print join ", ", after(qw< z a b c d e f g >); print join ", ", after(qw< f a b c d e f g >); print join ", ", after(qw< g a b c d e f g >); print join ", ", after(qw< a a b c d e f g >); __END__ d, e, f, g f, g g b, c, d, e, f, g

    - tye        

Re: flip-flop flop (be aware!)
by JavaFan (Canon) on Mar 30, 2012 at 15:09 UTC
    Two things. First, your use of EXPR .. 1 contains an error. If $. == 1, the flip-flop will fail prematurely. Better would be to use a regexp that can never match. Second, an easy way to "reset" the flip-flop is by using eval:
    sub blah { my $look_for = shift; my (undef, @a) = eval 'grep {$_ eq $look_for .. /(*FAIL)/} @_'; @a; }
    The eval forces a new flip-flop operator each time.
Re: flip-flop flop
by remiah (Hermit) on Mar 30, 2012 at 23:03 UTC

    I didn't know that if the right hand (reset) expression doesn't match, flip-flop to keep it's state, like closure.

    How about putting sentinel to the list, which surely reset flip-flop state?

    sub blah3 { my($look_for, @array)=@_; my @elements_we_want = grep { $_ eq $look_for .. $_ eq 'MY SENTINEL'} @array,'MY SENTINEL +'; return @elements_we_want[ 1 .. $#elements_we_want -1]; }
    not elegant? I like flip-flop since Marshall introduced me at Parsing a file and finding the dependencies in it

Re: flip-flop flop
by Anonymous Monk on Mar 30, 2012 at 21:35 UTC
Re: flip-flop flop
by Anonymous Monk on Mar 30, 2012 at 23:07 UTC
    Another attempt:
    sub after{ my $look_for = shift; my $i = 0; my $x = -@_; while(++$x){ return @_[$i..$#_] if $_[$i++] eq $look_for; } return; } @a = 'a'..'z';; print after( 'm', @a ); # n o p q r s t u v w x y z