Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

flag function

by dideod.yang (Sexton)
on Jul 11, 2018 at 10:57 UTC ( [id://1218301]=perlquestion: print w/replies, xml ) Need Help??

dideod.yang has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks. I have a question about function. I code some script using perl often. Below script, script open text file that include many words 'test'. but I want to print only one when script meet 'test' at first. so I always use $flag. however that script looks bad and uncomfortable. Can you recommand new functions? or tips? about that issue??
open TEST,"@ARGV[0]"; $flag ="ON" while(<TEST>){ if(/test/ and $flag eq "ON"){print "start";$flag ="OFF"}} close TEST

Replies are listed 'Best First'.
Re: flag function
by hippo (Bishop) on Jul 11, 2018 at 11:11 UTC

    TIMTOWTDI but I might write it like this:

    #!/usr/bin/env perl use strict; use warnings; open my $test, '<', $ARGV[0] or die "Cannot open $ARGV[0]: $!\n"; while (<$test>) { next unless /test/; print "start"; last; } close $test;
Re: flag function
by haukex (Archbishop) on Jul 11, 2018 at 11:27 UTC

    Using a variable to keep state is a perfectly acceptable solution. But since TIMTOWTDI, here's one way to do the same using the flip-flop operator. I've also made several stylistic suggestions, since that's what I find "uncomfortable" about your original code ;-)

    use warnings; use strict; my $file = $ARGV[0]; open my $fh, '<', $file or die "$file: $!"; while (<$fh>) { if ( (/test/..0) eq '1' ) { print "start\n"; } } close $fh;
Re: flag function
by ikegami (Patriarch) on Jul 11, 2018 at 21:13 UTC

    The part you're asking about is actually fine. On the other hand, just about everything else looks wrong to me. Let's go over the code.


    Always use use strict; use warnings qw( all );!!!


    open TEST,"@ARGV[0]";

    should be

    open(my $TEST, "<", $ARGV[0]) or die("Can't open \"$ARGV[0]\": $!\n");
    • Use the safer three-arg open over the two-arg open.
    • Always check open for errors as it is particularly prone to failure.
    • Don't use global variables for nothing.
    • Don't put variables in quotes for nothing.
    • @ARGV[0] is an array slice, but you want to lookup a single value in the array.

    $flag = "ON" ... $flag = "OFF"
    should be
    my $looking = 1; ... $looking = 0;
    • Your code didn't even compile.
    • Limit the scope of variables to where they are needed.
    • Use true and false values (such as 1 and 0) for booleans instead of two true values (such as "ON" and "OFF").
    • Use meaningful variable names.

    while (<TEST>)

    should be

    while (<>)

    <> is short for <ARGV>. ARGV is a specical file handle that reads from the files named in the argument list or STDIN if none.


    if (/test/ and $flag eq "ON")

    should be

    if ($looking && /test/)
    • It's better to only use the low precedence and, or and not for flow control (... or return ..., ... or die ..., ... or next ..., etc).
    • When you want to test for truthness, don't check for a specific value.
    • $looking is far cheaper than /test/, so we'll move it first.

    print "start";
    should be
    print "start\n";
    or
    say "start";

    close TEST;
    is useless if you don't check if it succeeded.
    So we now have
    use strict; use warnings qw( all ); my $looking = 1; while (<>) { if ($looking && /test/) { print "start\n"; $looking = 0; } }

    The one problem left is that you read "half" the file for nothing. Let's avoid that!

    use strict; use warnings qw( all ); while (<>) { if (/test/) { print "start\n"; last; } }

      ++ for a clear and thorough explanation but for completeness your last code block omits the declaration and initialization of $looking.

        That wasn't quite the problem, but it's been fixed. Thanks.

Re: flag function
by thanos1983 (Parson) on Jul 11, 2018 at 15:31 UTC

    Hello dideod.yang,

    Fellow Monks have answered your question but I wanted to add another possible solution, not more efficient but just for fun. See sample of code bellow:

    #!/usr/bin/perl use strict; use IO::All; use warnings; use Data::Dumper; use List::Util 'first'; my @lines = io($ARGV[0])->chomp->slurp; print Dumper \@lines; my $match = first { /test/ } @lines; print 'First Found: ' . $match . "\n"; __END__ $ perl sample.pl file.txt $VAR1 = [ 'This is line one', 'This is line two', 'This is line three with test key word', 'This is line four with test key word' ]; First Found: This is line three with test key word

    I am using the following two modules IO::All and List::Util::first. The first modules it is loading the lines on an array, the second module will stop searching the array when it will find the first match.

    Hope this helps, BR.

    Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: flag function
by Marshall (Canon) on Jul 11, 2018 at 17:12 UTC
    using a flag is fine. What you have is completely understandable, however your code will keep reading lines even after the flag is found. I would probably code something similar to what hippo++ did which uses last; to stop the loop once reading more lines has no useful purpose.

    However, something like this is also possible if the file is "small":

    print "start\n" if (grep {/test/}<TEST>);
    This would also read all of the lines in <TEST>. The scalar value of the grep is number lines containing "test". So you get one single print of "start" if any lines contain "test". Not the most efficient way, but also obvious to a Perl'er and fine if the file is "small".

    Another loop construct instead of "last" could be:

    while (<TEST> and !$flag){} #UPDATE---see below discussion!
    I would use something like that if the block of code is more than say 5 lines. Then I see that the loop will stop prematurely without having to read the body of the code and realize that there is a "last;" Of course I would name $flag to something like $test_seen.

    Update:
    oh, I see now: open TEST,"@ARGV[0]"; that would be flagged as an error if you have use strict; use warnings; in effect. Correct is $ARGV[0].

      while (<TEST> and !$flag){}

      Just a note: if you want to do something with the line read from the filehandle (and you probably want to set the flag at least), you can't just use <TEST>: it only works in a simple while, not in a more complex condition.

      1 while defined ($_ = <>) and not $seen = /test/; say $seen;

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        You are correct!
        I typed that in quicky from a cut-n-paste of OP's code without actually running a piece of code.

        For those interested, here are some redacted code examples to show what happens:

        while (<DATA> and !$flag){print;} error is: Use of uninitialized value $_ in print at C:\testwhile.pl li +ne 8, <DATA> line 1. while ($_=<DATA> and !$flag){print;} error is: Value of <HANDLE> construct can be "0"; test with defined() +at C:\testwhile.pl line 6. while (defined($_=<DATA>) and !$flag) no error
        I was impressed the first time that I saw error msg #2! Perl tells you exactly what to do and why. Impressive!
        Of course these are examples of why to always use strict; use warnings;!

        Update: A recent post my me that demo's this: Re: Search between pattern and append

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-03-29 10:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found