Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

A way to avoid repeated conditional loops

by Deus Ex (Scribe)
on Aug 24, 2011 at 10:32 UTC ( [id://922074]=perlquestion: print w/replies, xml ) Need Help??

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

Greetings fellow monks,

needing to parse some files containing lines like the following:

alpha # this line appears only once in the whole file beta beta beta beta beta beta ... beta

i wrote a loop like this:

.... while ( <FILE> ) { if ( /alpha/ ) { .... } else { .... } } ....

Since the condition gets encountered only once, and then for the next n-times it will be evaluated as false, here comes my question: is there a (possibly elegant) way to get rid of the conditional block (i.e.: without using a flag variable) once the match has happened for the only one time it will?

Thanks for your kind help

Replies are listed 'Best First'.
Re: A way to avoid repeated conditional loops
by tinita (Parson) on Aug 24, 2011 at 11:18 UTC
    use the flip-flop operator.
    if you want to read from alpha until end of file:
    while (<$fh>) { if ( (m/^alpha$/ .. eof($fh) ) > 1) { # everything between alpha and end of file } }
    you can even change that second condition to 0: if ( (m/^alpha$/ .. 0 ) > 1)

      That's an awesome approach! Really like this method. I'll test it and put it on production.

      Thanks!

        Are you assuming that 'alpha' is also always the first line in the file?

        Also, the posted flipflop doesn't behave the same as the OP

        A simpler way to just use a flag and short circuit the regex work:

        my $seen = 0; while (<$fh>) { if ( !$seen and m/^alpha$/) { $seen = 'yes indeed'; print "true\n"; }else{ print "false\n"; } }

        Given a test file of:

        foo bar alpha beta gamma delta
        The results I got are:
        Original: false false true false false false Flipflop: Argument "" isn't numeric in numeric gt (>) at test.pl line 21, <$fh> +line 1. false Argument "" isn't numeric in numeric gt (>) at test.pl line 21, <$fh> +line 2. false false true true true Flag: false false true false false false

Re: A way to avoid repeated conditional loops
by AR (Friar) on Aug 24, 2011 at 10:37 UTC
    ... while ( <FILE> ) { if ( /alpha/ ) { some code #1 last; } else { some code #2 } } while ( <FILE> ) { some code #2 } ....
      I wonder whether the copying of some code #2 is worth the avoidance of the test. Another way of writing it, with the same duplication:
      while (<FILE>) { if (/alpha/) { some code #1; while (<FILE>) { some code #2; } } else { some code #2; } }

      You can't rely on readline returning EOF or errors more than once. Fix:

      OUTER: { for (;;) { defined( my $_ = <$fh> ) or last OUTER; if (/alpha/) { f(); last; } else { g(); } } while (<$fh>) { g(); } }

      Update: The remainder of this post is wrong.

      You can get rid of the duplicate g() by making the loops bottom tested.

      OUTER: { defined( my $_ = <$fh> ) or last OUTER; for (;;) { if (/alpha/) { f(); last; } defined( $_ = <$fh> ) or last OUTER; } for (;;) { g(); defined( $_ = <$fh> ) or last OUTER; } }

      You can squish it down a bit:

      LOOP: { defined( my $_ = <$fh> ) or last; if (/alpha/) { f(); redo; } for (;;) { g(); defined( $_ = <$fh> ) or last; } }

      Look at that, g() is not even duplicated anymore!!

        Why is your for loop idiom better than AR's or JavaFan's while loop statements above?

        The squished version is really cool :) It looks I got my solution!

        Many thanks ikegami!

      If 'some code #2' is very small or can be packaged into a single function call, I might go with that. Or I might just make it obvious that we only want/expect this once in the one loop:
      my $got_alpha; while (<FILE>) { unless ($got_alpha) { if (/alpha/) { #...alpha code $got_alpha++; next; } } # ...more code }
      Update: And I just noticed the part in the OP where it says "without a flag variable"...oh, well...

        I think you missed the point of the original post. Deus Ex wants to get rid of running the conditional as soon as "alpha" is seen. Your code substitutes that for another conditional (that doesn't use the regex engine, but still).

      Interesting. I would never think of doing like this. Thanks :)

      The only point, at first glance, would be that you get twice the "while" loop. I'm trying to understand if you can avoid such "ugly" (take the word for it) structures. Thanks though.

      You can't rely on readline returning EOF or errors more than once.

Re: A way to avoid repeated conditional loops
by Eliya (Vicar) on Aug 24, 2011 at 19:37 UTC

    Yet another way would be to set up a function(ref) which you just call for every line.  Initially, the called function checks for /alpha/, but once alpha is encountered, it replaces itself with the routine do_beta that is then called directly for the remaining beta lines. That way, the condition is no longer tested from there on.

    my $f; $f = sub { if (/alpha/) { do_alpha(); $f = \&do_beta; } else { do_beta(); } }; while (<DATA>) { $f->(); } sub do_alpha { print "do_alpha(): \$f = $f, \$_ = $_"; } sub do_beta { print "do_beta(): \$f = $f, \$_ = $_"; } __DATA__ beta beta beta beta alpha beta beta beta beta beta beta beta beta

    Output:

    do_beta(): $f = CODE(0x796e88), $_ = beta do_beta(): $f = CODE(0x796e88), $_ = beta do_beta(): $f = CODE(0x796e88), $_ = beta do_beta(): $f = CODE(0x796e88), $_ = beta do_alpha(): $f = CODE(0x796e88), $_ = alpha do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta do_beta(): $f = CODE(0x7c6ee8), $_ = beta

    As you can see, the coderef changes after alpha has been encountered.

    (upd: as it seems, BrowserUk posted essentially the same idea while I wrote up my reply...)

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: A way to avoid repeated conditional loops
by Marshall (Canon) on Aug 24, 2011 at 13:45 UTC
    The below is one way:
    Keep things simple.
    Output of below is: term 'alpha' detected on input line 4
    The code below is very efficient and runs fast.
    #!usr/bin/perl -w use strict; while (<DATA>) { if(/\balpha\b/) { print "term 'alpha' detected on input line $.\n"; next; } .. do something here... .. sometimes better than elsif() } __DATA__ beta beta beta alpha # this line appears only once in the whole file beta beta beta
    if you have a lot of conditions, then perhaps something like this is appropriate:
    while ( <FILE> ) { if ( /alpha/ ) { .... next; } if (/beta/) { .... next; } #here is the default code... }
    One of the reasons to do it that way is so that all of the conditions can be interchanged with one another. If you have an if(..)elsif(..)else(..) conditional, this hinders the ability to change the order of the conditions. At the end of the day, that approach will result in more complex code and limit the ability to re-order the code's priority of matching - don't do it. At least for number of "if" statements of about 5-6.

    Update: Well this is one of those situations where there just isn't a "one size fits all" solution. I tend to prefer coding with several sequential if{..} statements so that all of them are "equal". This whole coding style discussion can set off something that will last until a gallon of gas falls back to $1.60.

Re: A way to avoid repeated conditional loops
by BrowserUk (Patriarch) on Aug 24, 2011 at 19:26 UTC

    You can always disable the warning if it bothers you. Of course, its only a significant gain if the alpha appears early in the file. And, the sub overhead may kill the gain, if the processing involved isn't very significant:

    #! perl -slw use strict; sub doBeta(_) { print 'Beta'; } sub doit(_) { if( /alpha/ ) { print 'Alpha'; *doit = \&doBeta; return; } doBeta( $_ ); } doit( $_ ) while <DATA>; __DATA__ beta beta beta beta beta beta beta beta beta alpha beta beta beta beta beta beta beta beta beta beta beta beta

    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.
Re: A way to avoid repeated conditional loops
by Not_a_Number (Prior) on Aug 24, 2011 at 19:08 UTC

    Hi.

    Frankly, if your dataset is as described in your OP, of all the solutions posted above, I prefer your original one!

    It has two major advantages over many of the other solutions provided:

    1. It works!

    2 . It is extremely comprehensible.

Re: A way to avoid repeated conditional loops
by TomDLux (Vicar) on Aug 24, 2011 at 19:25 UTC
    use autodie; open my $file, '<', $filepath; process_file_header( $file ); proress_file_remainder( $file );

    As Occam said: Entia non sunt multiplicanda praeter necessitatem.

Re: A way to avoid repeated conditional loops
by locked_user sundialsvc4 (Abbot) on Aug 24, 2011 at 12:22 UTC

    Call it “a flag variable” if you want to, but this is a classic place for a finite-state machine (FSM) algorithm.   I presume that you are familiar with the term.

    Edit:   One subtle distinction that sometimes comes in handy with FSM algorithms is to put “read the next line” as a subroutine-call that occurs within each state-handler, instead of structuring the algorithm within a while-loop as shown above.   Sometimes you might want to loop through more than one consecutive state while processing the same line.

Re: A way to avoid repeated conditional loops
by Anonymous Monk on Aug 25, 2011 at 10:20 UTC

    Use m?alpha? instead of m/alpha/ to match only once:

    #!/usr/bin/perl use warnings; use strict; while (<DATA>) { if (m?alpha?) { print "+ $_"; } else { print "- $_"; } } __DATA__ beta beta alpha beta beta alpha alpha beta

    Results in:

    - beta - beta + alpha - beta - beta - alpha - alpha - beta

      It would be a nice trick, but the match is just ignored after the first time, so the conditions keeps getting evaluated.

      Thanks though

        =~ s/^alpha/^#alpha/ ... and now do what you want with the whole file

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (2)
As of 2025-11-15 14:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What's your view on AI coding assistants?





    Results (72 votes). Check out past polls.

    Notices?
    hippoepoptai's answer Re: how do I set a cookie and redirect was blessed by hippo!
    erzuuliAnonymous Monks are no longer allowed to use Super Search, due to an excessive use of this resource by robots.