http://www.perlmonks.org?node_id=20078

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

# this program reads through a file # line by line and prints "PRINTING" # for every line between $START & $STOP # i'm intersted in improving my function &flip(\$flop) $START = 'START'; $STOP = 'STOP'; $flop = 0; while(<>) { my @record = split /,/; chomp($record[0]); if (($flop) && ($record[0] ne $STOP)){ print "PRINTING"; } else { print "not printing"; } if (($record[0] eq $START) || ($record[0] eq $STOP)) { &flip(\$flop); } } sub flip {$ref = @_[0]; $$ref = !($$ref)}
as you can see, the function &flip(\$flop)
is doing alright and working ... there's 
absolutly no reason to improve it... 

in fact, if i just wrote 
$flop = !($flop)
i wouldn't need a function at all 
(i also wouldn't get to have the word 
'flipflop' actually working in my code
  ... i think on some level it's like the 
 poetry to me)

for some reason i'd really like to optimize 
the function a step further to only ONE 
statement
 
i tried 
sub flip { $@_[0] = !($@_[0] };
figuring that it would be like saying:
"de-reference the variable in position
 zero of the 'magic' array and toggle it"

unfortunately it's more like saying: 
"don't compile"

why is that?

Replies are listed 'Best First'.
Re: something about improving code...
by btrott (Parson) on Jun 28, 2000 at 00:56 UTC
    You wrote:
    > i tried > sub flip { $@_[0] = !($@_[0] };
    So close. :) Try this:
    sub flip { ${ $_[0] } = !${ $_[0] } }
    Alternatively, check out Tie::FlipFlop on CPAN. It uses tie to automatically take care of a flipflop state, and it lets you use other values than 0 and 1, as well. It's very cool. And you also don't have to call a flip function, because tie handles it all automagically.

    Here's a snippet from the docs:

    use Tie::FlipFlop; tie my $flipflop => Tie::FlipFlop => qw /Red Green/; print $flipflop; # Prints 'Red'. print $flipflop; # Prints 'Green'. print $flipflop; # Prints 'Red'.
Re: something about improving code...
by merlyn (Sage) on Jun 28, 2000 at 01:12 UTC
    You have something against this simpler code?
    $START = 'START'; $END = 'END'; while (<>) { if (/$START/../$END/) { print "inside\n"; } else { print "outside\n"; } }
    This seems much simpler. Don't reinvent the scalar dot-dot operator!

    -- Randal L. Schwartz, Perl hacker

      To e more precise to the problem at hand ( i.e. the program given by the asker of the question was 'not printing' when a line containing 'start' or 'stop' was encoutered ):
      my( $START, $STOP ) = qw( START STOP );
      while ( <DATA> ) {
        if ( /$START/ .. /$STOP/ ) {
          print /(?:$START|$STOP)/ ?  "not printing\n" : "PRINTING\n";
        } else {
          print "not printing\n";
        }
      }
      __END__
      Hello,
      I like to START
      something and
      not STOP it
      properly.
      
      
      ---
      output:
      
      not printing
      not printing
      PRINTING
      not printing
      not printing
      
      
      Enjoy!
      --
      Casey
      
        Even that has a simpler plan:
        my ($START, $STOP) = qw(START STOP); while (<DATA>) { $where = /$START/../$STOP/; if ($where and $where > 1 and $where !~ /E0/) { print "PRINTING\n"; } else { print "not printing"; } } __END__

        -- Randal L. Schwartz, Perl hacker

Benchmarking
by Aighearach (Initiate) on Jun 28, 2000 at 08:57 UTC
    Lets take a look at the efficiency of using a sub vs. not using a sub. Efficiency is an important part of elegance, and so you should be aware of the performance differences when trying to find the most aesthetic way to code something.

    Here is the code I used to benchmark. To give the best case, I've used reptile's function code.

    use Benchmark; $n = 1; timethese( 0, { 'sub' => 'flipflop($n)', 'norm' => '$n = !($n)' } ); sub flipflop { $_[0] = !($_[0]) }

    And the output:

    Benchmark: running norm, sub, each for at least 3 CPU seconds...
          norm:  4 wallclock secs ( 3.24 usr +  0.00 sys =  3.24 CPU) @ 679799.69/s (n=2202551)
           sub:  3 wallclock secs ( 3.16 usr +  0.00 sys =  3.16 CPU) @ 169643.35/s (n=536073)
    

    And so in this case it is a whopping 4.007232836 times faster not to use a function call.

    But, is reptile's code faster than raflach's? raflach's code is shorter, after all. But, it does have to return a value. Lets test it and see.

    use Benchmark; $n = 1; timethese( 0, { 'sub' => '$n = flip($n)', 'norm' => '$n = !($n)' } ); sub flip { !($_[0]) }

    and the results...

    Benchmark: running norm, sub, each for at least 3 CPU seconds...
          norm:  3 wallclock secs ( 3.17 usr +  0.00 sys =  3.17 CPU) @ 696554.57/s (n=2208078)
           sub:  4 wallclock secs ( 3.26 usr +  0.00 sys =  3.26 CPU) @ 118730.98/s (n=387063)
    

    So, ditching the sub and just flipping the value by hand is 5.866662349 times faster this time. Looks like having to return and adding is less efficient.

    In most cases, I would highly recommend against using the refs passed to a function to alter those variables directly, but in this case I don't think there is any confusion generated.

    Yes, I know that performance isn't everything.

    note: these tests performed on an AMD K6-II/400 w/128 megs SDRAM, running linux 2.2.12. Your mileage may vary.

    Paris Sinclair    |    4a75737420416e6f74686572
    pariss@efn.org    |    205065726c204861636b6572
    I wear my Geek Code on my finger.
    
      This was exactly the point of my previous post. Thank you for running a benchmark. I'd be interested in comparing the efficiency of the .. range operator vs using the flip flop method (inlined rather then as a subroutine). After all, this whole thread is about efficiency.

      BTW: for those people interested in benchmarking methods, there is a great post called Benchmarking your code written up by turnstep.

        I wanted to benchmark the golf game, too, but as it all uses a while on a filehandle, I didn't see a way to do it without inserting other factors.

        Anybody know how to do this?

        Paris Sinclair    |    4a75737420416e6f74686572
        pariss@efn.org    |    205065726c204861636b6572
        I wear my Geek Code on my finger.
        
Re: something about improving code...
by reptile (Monk) on Jun 28, 2000 at 02:30 UTC

    For your flip, you don't even need to use a reference. Modifying @_ in a subroutine also modifies the variables in the argument list. You could say this:

    sub flip { $_[0] = !($_[0]) } # $foo will be modified below flip($foo);

    I'm not positive about this but AFAIK since that's a one line subroutine, perl will optomize by inlining the subroutine, and be even faster. Still, $foo = !$foo; isn't that hard to write directly. Whatever you prefer.

    72656B636148206C72655020726568746F6E41207473754A

Re: something about improving code...
by ncw (Friar) on Jun 28, 2000 at 01:12 UTC
    You'd be wanting this I expect...
    sub flip { ${$_[0]} = !${$_[0]} };
    The precedence of de-reference $ and array subscript [] mean that what you wrote doesn't work. You need those ${ ... } de-reference brackets for all de-references except the simplest.

    Note also that you meant $_[0] rather than @_[0] didn't you ;-)

    Interestingly

    sub flip { $$_[0] = !($$_[0]) }
    Isn't a syntax error and doesn't produce any warnings but does something completely unintended.
    perl -w -e 'use strict; sub flip { $$_[0] = !($$_[0]) }; my $z=1; fl +ip(\$z); print "$z\n"' 1
    I don't understand why this doesn't print use of unitialised variable or something like that...
RE: something about improving code...
by Adam (Vicar) on Jun 28, 2000 at 00:57 UTC
    your sub flip won't work because you have missmathed parens. :)
    More importantly, if you really want to optimize then don't use a subroutine there. Subroutines have a certain amount of overhead that you don't need here. I would just inline the $flop = !($flop)
Re: something about improving code...
by raflach (Pilgrim) on Jun 28, 2000 at 01:34 UTC
    how about
    sub flip { !($_[0]) }
    and in your main body just do
    $flop=flip($flop);