Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Multi-stage flip-flop?

by RonW (Parson)
on Dec 10, 2014 at 23:30 UTC ( #1109986=perlmeditation: print w/replies, xml ) Need Help??

In a response to a SOPW post, I wrote:
while (<>) { if (/^#ERRORS/ .. /^CELLS/) { push(@errors, $_); } elsif (/^CELLS/ .. /^\s*$/) { push(@errors, $_); } else { ...; # other processing } }

Which doesn't work.

Occurs to me that it might be a useful enhancement to allow .. conditionX to be an additional stage to the preceding condition1 .. condition2, creating a linked cascade of flip-flops:

if (/match1/ .. /match2/) { doStage1(); } elsif (.. /match3/) { doStage2(); } elsif (.. /match4/) { doStage3(); } else { doOtherProcessing(); }

The semantics would be an extension of .. In the second example, match1 would trigger stage 1 (doStage1() will be called). Then match2 will end stage 1 and trigger stage 2 (doStage2() will be called). Then match3 ends stage 2 and triggers stage 3 (doStage3() will be called). Finally, match4 resets the cascade.

Thoughts?

Updated to mention the first example doesn't work.

Updated second example and the description of the semmantics.

Replies are listed 'Best First'.
Re: Multi-stage flip-flop?
by roboticus (Chancellor) on Dec 11, 2014 at 01:28 UTC

    RonW:

    Here's an alternative, though the syntax isn't as nice as your proposed syntax:

    #!/usr/bin/env perl use strict; use warnings; my $fn = \&doOtherProcessing; while (<DATA>) { if (/for/) {$fn = \&doStage1} if (/their/) {$fn = \&doStage2} if (/fruit/) {$fn = \&doStage3} if (/lazy/) {$fn = \&doOtherProcessing} $fn->($_); } sub doStage1 { print "1: ",shift } sub doStage2 { print "2: ",shift } sub doStage3 { print "3: ",shift } sub doOtherProcessing { print "*: ",shift } __DATA__ now is the time for all good men to come to the aid of their party. Time flies like an arrow fruit flies like a banana. The quick red fox jumps over the lazy brown dog. Etaoinshrdlu.

    Basically we're just using $fn to be a combination state variable / function pointer to track the current state of your chain of comparisons, and selecting a different handler at each match:

    *: now is the time 1: for all good men to 1: come to the aid of 2: their party. 2: Time flies like an arrow 3: fruit flies like a banana. 3: The quick red fox *: jumps over the lazy *: brown dog. *: Etaoinshrdlu. *:

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Almost. Your code is not qualifying the state before changing it.

      Maybe like this:

      my @fn = (\&doOtherProcessing, \&doStage1, \&doStage2, \&doStage3); my $fn = 0; while (<DATA>) { if ($fn == 0 and /for/) {$fn = 1} if ($fn == 1 and /their/) {$fn = 2} if ($fn == 2 and /fruit/) {$fn = 3} if ($fn == 3 and /lazy/) {$fn = 0} $fn[$fn]->($_); }

        RonW:

        Good catch! I won't update my post, as you've already posted a suitable fix.

        ...roboticus

        When your only tool is a hammer, all problems look like your thumb.

Re: Multi-stage flip-flop?
by LanX (Sage) on Dec 11, 2014 at 14:31 UTC
    would this dispatcher like syntax be OK for you?

    till ( - /match0/ => \&doOtherProcessing, - /match1/ => \&doStage1, - /match2/ => \&doStage2, - /match3/ => \&doStage3, )

    of course you are also free to write something like

    till ( - /match0/ => sub { print "default Processing" }, - yadda yadda ... )

    please note that the minus is obligatory to:

    • enforce scalar context around the /match/
    • avoid "empty" values which are ignored in lists│

    Furthermore you'll need to bind the state╣ of till() to it's position, i.e. using caller to determine filename and line number of invocation.

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

    ╣) e.g. the $fn here

    ▓) Consequently you shouldn't use two till()'s within the same line, otherwise they'll share the same state.

    │) minus transforms true => -1 , false => 0

      Proof of concept:

      use strict; use warnings; use Data::Dump; { my %state; sub till { my @phases=@_; my ($file,$line)= (caller)[1..2]; my $state = \$state{$file,$line}; $$state //= 0; $phases[1+$$state]->($_); if ($phases[$$state]) { $$state += 2; $$state %= @phases; } } } sub doStage1 { print "1: ",shift } sub doStage2 { print "2: ",shift } sub doStage3 { print "3: ",shift } sub doOtherProcessing { print "*: ",shift } while (<DATA>){ till ( - /for/ => \&doOtherProcessing, - /their/ => \&doStage1, - /fruit/ => \&doStage2, - /lazy/ => \&doStage3, ); } __DATA__ now is the time for all good men to come to the aid of their party. Time flies like an arrow fruit flies like a banana. The quick red fox jumps over the lazy brown dog. Etaoinshrdlu.

      output:

      *: now is the time *: for all good men to 1: come to the aid of 1: their party. 2: Time flies like an arrow 2: fruit flies like a banana. 3: The quick red fox 3: jumps over the lazy *: brown dog. *: Etaoinshrdlu.

      Cheers Rolf

      (addicted to the Perl Programming Language and ☆☆☆☆ :)

        After refining my code from Re^6: Multi-stage flip-flop?, my proof of concept where conditions are only evaluated in the corresponding state.

        To do: Add tests for code and scalar based conditions.

      Can a "bare" regex be used like that? I would have thought it would need quoting.

      So maybe:

      till ( qr/match0/ => \&doOtherProcessing, qr/match1/ => \&doStage1, )
        That's completely different, qr// returns a regex and not the result of a boolean test.

        Cheers Rolf

        (addicted to the Perl Programming Language and ☆☆☆☆ :)

Re: Multi-stage flip-flop?
by LanX (Sage) on Dec 11, 2014 at 01:09 UTC
    if I understand you right this can be achieved with a function mff() (multi_flip_flop) encasulating the logic and returning the stage number starting with 1.

     (\&doOtherProcessing, \&doStage1(), \&doStage2(),  \&doStage3())[ mff(/match1/, /match2/, /match3/, /match4/) ] ->()

    or

    if ( my $stage = mff(/match1/, /match2/, /match3/, /match4/) ) { if ( $stage == 1 ) { ... } elsif ( $stage == 2) { ... } ... } else { doOtherProcessing(); }

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

Re: Multi-stage flip-flop?
by LanX (Sage) on Dec 10, 2014 at 23:39 UTC
    I don't understand, your first example looks wrong.

    With Flip/flops the borders are inclusive, i.e. the second condition wouldn't be evaluated if the start still belongs to the first condition.

    Cheers Rolf

    (addicted to the Perl Programming Language and ☆☆☆☆ :)

      I didn't say that it worked. Only that I wrote it

      Anyway, is an example of why multi-stage flip-flops would be a useful enhancement.

        Please try to give an example highlighting what you mean.

        Cheers Rolf

        (addicted to the Perl Programming Language and ☆☆☆☆ :)

Re: Multi-stage flip-flop?
by Anonymous Monk on Dec 11, 2014 at 01:15 UTC

    "Each ".." operator maintains its own boolean state, even across calls to a subroutine that contains it." (source) How would two independent operators know they are "linked"? What are the scoping rules for this? Sounds too magical for me. Probably easier to just stick to a classic state machine design...

      If you use closures, you can force a different internal state for each range operator, e.g.:
      sub mkrange { my ($r1, $r2) = @_; return sub { /$r1/../$r2/ }; } my $range1 = mkrange(qr/FIRST/, qr/SECOND/); my $range2 = mkrange(qr/THIRD/, qr/FOURTH/); while (<DATA>) { if ( $range1->() ) { print "R1: $_"; } if ( $range2->() ) { print "R2: $_"; } } __END__ junk FIRST 1 2 3 SECOND junk THIRD 4 5 6 FOURTH junk

      Within the same block, .. conditionX associates with the nearest condition1 .. condition2 lexically above it. So in:

      if (cond1 .. cond2) { if ( .. condA) { } } if ( .. cond3) { } if (cond4 .. cond5) { if (condB .. condC) } if ( .. cond6) { }

      .. cond3 would associate with cond1 .. cond2

      .. condA would not, because its in a different block (it would be an orphan)

      .. cond6 would associate with cond4 .. cond5 (it would not "see" condB .. condC because that is in a different block)

      In terms of parsing, the handler for .. conditionX would search backwards until it found a conditionV .. conditionW or the beginning of the block. Then the conditionV .. conditionW would be "informed" of the .. conditionX

      (Updated to add paragraph about parsing)

Re: Multi-stage flip-flop?
by sundialsvc4 (Abbot) on Dec 11, 2014 at 13:56 UTC

    On any code-review, I would give thumbs-up to the “state-machine driven” approach, and I always thumbs-down the use of Perl flip-flops.   They are one of the very few “IMHO, really bad ideas” in this language, although there is nothing to be done about them now (except to not use them).   At one time, they might have been seen as desirable because they let you feed a few fewer inches of paper-tape into your teletype machine, but to me they are extremely obtuse.   You cannot readily look at the statement and see what it is going to do, well, “next.”   Nor why.

    The other approach, on the other hand, is both perfectly-clear to read, and easily maintainable.   First, the logic decides what to do next, based only upon what it sees and upon the value of variables whose values can easily be printed.   (There are no “side effects.”   It’s all right there in front of you.)   Then, separately, it carries out the appropriate action for that case.   What will the behavior be?   It’s clear.   How do I change it, and how to I demonstrate its new correctness?   Again, clear.   That’s the kind of code you want to be in your repository.

      Perl flip-flop seems to be a generalization of range matching like in awk, sed, SNOBOL and others.* (Generally "rule-action" languages.)

      I can see where the generalization may have gone too far, but in the context of regular expressions, syntax like:

      /match0/ .. /match1/

      seems clear to me. Or perhaps the awk/sed syntax:

      /match0/, /match1/

      might be clearer - except that it would be inconsistent with other uses of , in Perl.

      ---

      * I learned about SNOBOL long ago when I took a "300 level" CS class called "Language Stuctures". It was a very interesting class. Learned about several weird languages, including ProLog, LISP and APL.

      Relevant to sundialsvc4's comment "based only upon what it sees and upon the value of variables whose values can easily be printed", many textual state machine description syntaxes do not declare an explicit state variable.

      For example, in some dialects of Scheme, this:

      (define mystm (automaton init [init : (a -> slurp)] [slurp : (b -> done) (* -> slurp)] [done : success] ) )

      defines a state machine equivalent to a Perl flip-flop. ("success" resets the state machine and returns true to the parent task.)

      In cond0 .. cond1 the states are anonymous, but cond0 is the "event" instigating the transition from the initial "inactive" state to the "active" state and cond1 is the event instigating the transition back to "inactive".

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (2)
As of 2021-10-25 05:15 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My first memorable Perl project was:







    Results (89 votes). Check out past polls.

    Notices?