Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

flip-flop operator and sequence number

by iakobski (Pilgrim)
on Jun 28, 2001 at 17:15 UTC ( [id://92268]=perlquestion: print w/replies, xml ) Need Help??

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

I'm not sure if the title is any good but I can't think of a better description.

I have a bit of code that parses a file and does something for a block of lines in the middle. This is a good candidate for the flip-flop operator and I coded my loop like this:

foreach (grep /start/ .. /stop/, @data){ # do_stuff }
Which is fine, but then I noticed it is not supposed to do_stuff on the boundaries. My question is: is there a better way of doing this than:
foreach (grep /start/ .. /stop/, @data){ next if /start/; last if /stop/; # do stuff }
The regexes are a lot more involved in real life. I don't like the idea of copying chunks of code either.

I tried using the sequence counter, but it was not visible outside the grep...can anyone explain this too?

-- iakobski

Replies are listed 'Best First'.
Re: flip-flop operator and sequence number
by chipmunk (Parson) on Jun 28, 2001 at 18:02 UTC
    The return value from the flip-flop operator (in scalar context) actually gives you the info you need to skip the first and last values matched.

    When the flip-flop operator in scalar 'flips' (when the left hand side is true), it starts at 1 and counts up until it 'flops' (when the right hand side is true). So, skip to the next element when the return value equals 1.

    Also, when the flip-flop operator 'flops', it appends 'E0' to the return value. For example, if it finds five matches in a row, it will return (1, 2, 3, 4, '5E0'). So, you should also skip to the next element when the return value contains an E.

    Here's how this might be coded:

    foreach (@data){ next unless $check = /start/ .. /stop/; next if $check == '1' or $check =~ /E/; # do stuff }
Re: flip-flop operator and sequence number
by merlyn (Sage) on Jun 28, 2001 at 18:23 UTC

      Cool... And how about this one?

      foreach (sub {pop;shift;@_}->(grep /start/../stop/, @data) ) { ... }

      Sorry, just kidding :-)

      UPDATE: taking Abigail and John M. Dlugosz's remarks into account, the original post wanted to stop after the first occurence of 'stop'. I guess we should then be using ?? instead of // to prevent matching more than once.

      foreach (sub {pop;shift;@_}->(grep ?start?..?stop?, @data) ) { ... }

      --bwana147

        That code doesn't do the same.
        #!/opt/perl/bin/perl -wl use strict; my @data = qw /foo start bar baz stop qux start quux stop fuzzle/; foreach (grep /start/ .. /stop/, @data) { next if /start/ || /stop/; print } print "----"; foreach (sub {pop; shift; @_} -> (grep /start/ .. /stop/, @data)) { print; } __END__ bar baz quux ---- bar baz stop start quux
        You are losing the boundaries of the flip-flop operator, and use the boundaries of grep. But those boundary events are not necessarely the same.

        -- Abigail

        I love it! Using the sub{}->() idiom you can modify a list in-place without using a temporary. Your code does the same as:
        my @temp= grep /start/../stop/, @data; pop @temp; shift @temp; foreach (@temp) { ... }
        right?

        —John

      .. not being able to do, reduced to nitpicking. Sorry your answer was so beautiful I studied it but.. perl -e '@data=qw(1 2 3 4 5 6 7 8 9 10 11); foreach (grep {not (/1/../3/) =~ /^1?$|E/ } @data) { print "o> $_\n"; } '
      prints
      o> 2
      o> 11

      I hope I'm wrong..

        Thats looks like the correct output to me. What were you expecting to see?

        Update: Ok. Getting downvotes, so perhaps I'll be a little clearer.

        Your array contains the numbers from 1 to 11. Your flip-flop condition is /1/ .. /3/. These are regular expressions and match if the values they are matched against contain this string in any position. Obviously it switches on when it gets to 1 and switches off when it gets to three. It then switches on again when it gets to 10. It would have switched off again had your array gone up to 13.

        The problem is therefore not with merlyn's algorithm, but with your implementation of it :)

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

        Perl Training in the UK <http://www.iterative-software.com>

      mattr is right, merlyn's solution does not work.

      But this modification does:

      foreach ( grep { (/start/../stop/) !~ /^1?$|E/} @arry ){ #... }
      I looked a bit further, and what is coming out of the (/start/../stop/) is the sequence number...until you put the not in front of it. Then it just yields blank or 1!

      Any ideas why? Is it being evaluated in list context? Why does the not have any influence on the context?

      -- iakobski

        Uh, it is working. The start match is /1/, which also triggers on "10". So the output is:
        1 start - don't show 2 in - show 3 end - don't show 4 out 5 out 6 out 7 out 8 out 9 out 10 start - don't show 11 in - show
        It's exactly right. My solution works fine, no need to modify it.

        -- Randal L. Schwartz, Perl hacker

        not puts things in a boolean context, either true or false. False is 0 and true is anything else (in numerical context at least), by convention we use 0 and 1 as false and true when we aren't concerned about data, just truthfulnes.

        The 15 year old, freshman programmer,
        Stephen Rawls

        That's a good observation on what not does.

        But, the binding operator =~ has a higher order of precidence, so the not applies to the result of the match, not to the left hand side being matched.

        —John

Re: flip-flop operator and sequence number
by bwana147 (Pilgrim) on Jun 28, 2001 at 18:00 UTC

    To use the sequence counter, I guess you'd use something like this:

    foreach ( @data ) { my $seq = /start/ .. /stop/; next unless $seq > 1 && substr($seq, -2) ne 'E0'; # do_stuff }

    Not tested, but you get the idea...

    --bwana147

Re: flip-flop operator and sequence number
by Fool on the Hill (Acolyte) on Jun 28, 2001 at 18:25 UTC
    Hello, You can put real code into the grep so that it looks this this. That way you can access the sequence number you are refering to. The sequence counter is not visible because grep just uses whether the block evaluates to true or false for the list value (I think!)
    #!/usr/bin/perl -w use strict; my @data=(<DATA>); foreach (grep { my $sequence_number=/START/ .. /STOP/; $sequence_numbe +r ne 1 and $sequence_number !~ /E0/ and $sequence_number;} @data ) { chomp; print "$_ Between START and STOP\n"; } __END__ START Sausage Mash Mushy Peas STOP Monkey Nuts START Bananas Custard STOP Tarzan
Re: flip-flop operator and sequence number
by John M. Dlugosz (Monsignor) on Jun 28, 2001 at 19:02 UTC
    I don't like the idea of copying chunks of code either.

    I notice you already got answers on how to tell when it flips or filter out the first/last without retesting, but I wanted to make a note on this specific point of repreating the RE.

    my $start= qr/start/; # note the regex's once my $stop= qr/stop/; foreach (grep /$start/ .. /$stop/, @data) { next if /$start/; last if /$stop/; # ....
Re: flip-flop operator and sequence number
by mattr (Curate) on Jun 28, 2001 at 18:14 UTC
    How about
    # variation with grep @a=qw(abc def ghi jkl mno pqr stu vwxyz); @g = grep /i/../u/, @a; for (1..$#g-1) { print "$g[$_]\n"; } # with map @a=qw(abc def ghi jkl mno pqr stu vwxyz); $flag=-1; sub go { print "$_\n" if ($flag>0); if ($_ =~ /i|u/) { $flag = $flag * -1 } } @g = map{ &go($_) => $_ } @a;
    Just checked, it does not work if you say qw(abc start some things to print stop vwxyz) and look for start|stop but maybe that's not how you're using it. Does seem to work (checked the grep version) for qw(abc def start ghi jkl xxx mno pqr stu stop vwxyz).
    Updated: You guys are so right. Kudos and votes to you.
Re: flip-flop operator and sequence number
by gaudior (Pilgrim) on Jun 28, 2001 at 23:54 UTC

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2024-04-20 03:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found