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


in reply to improve ugly flow control

Last Update: This is wrong. See below for details.

Here's a somewhat exotic construction that I think reads well. Assumes that do_something() returns true on success.

log_failure() unless grep { exists($hash{$_}) && do_something($_) .. 1 } @options;
or maybe better,
grep { exists($hash{$_}) && do_something($_) .. 1 } @options or log_failure();
Untested, but I think it's nearly right.

The flipflop is true for zero or one keys and the initiating condition is not evaluated again after it is first true.

Update: Tested now, and it's not right. Trying to fix it.

Another Update: Is this a bug in perl's flipflop op? In perl-5.8.4

$ perl -Mstrict -we'my $s=1;for(qw/foo bar baz/){ print if /b/..$s }' barbaz$ $ perl -Mstrict -we'my $s=1;for(qw/foo bar baz/){ print if $s../b/ }' $
I'm using a variable $s there because bart++ tells me that flipflop can play games with $. if bare integers are presented to it.

Final Update: There is no bug, except in my logic. Changing the right hand side of the flipflop to match anything (//) instead of fooling with constants, I find by printing the sequence numbers that the flipflop switches off ok, but switches on again next time around.

$ perl -e'for (qw/foo bar baz blah/){print int scalar(/b/..//)}' 0111$
Silly mistake on my part.

Here is a version that works, but it still needs that pesky $success variable I was trying to get rid of.

{ my $success; grep { ! $success && exists($hash{$_}) && do_something($_) .. ($success = 1) } @options or log_failure(); }
$success must be checked first to short-circuit the rest of the processing.

After Compline,
Zaxo

Replies are listed 'Best First'.
Re^2: improve ugly flow control
by Aristotle (Chancellor) on Sep 19, 2004 at 08:01 UTC

    Neat hack. Note that the grep will always iterate all of @options though. Also, you can remove the dependency on do_something()'s return value with a construct like

    exists $hash{ $_ } and ( do_something( $_ ), 1 )

    Makeshifts last the longest.

Re^2: improve ugly flow control
by Aristotle (Chancellor) on Sep 19, 2004 at 23:08 UTC

    In reply to the update:

    // does not match "anything". It is overloaded to reapply the last successfully matched pattern, in your case, /b/. You wouldn't notice that because all your test data past the successful match has a "b" in it… Changing that clearly reveals faulty logic: once the right side is true, and flipflop starts over on the left side again.

    $ perl -le'for( qw/foo bar baz blah/ ){ print int scalar( /b/ .. // ) +}' 0 1 1 1 $ perl -le'for( qw/foo bar baz quux/ ){ print int scalar( /b/ .. // ) +}' 0 1 1 0

    Now let's take a short trip to perlop:

    The value returned is either the empty string for false, or a sequence number (beginning with 1) for true. The sequence number is reset for each range encountered. The final sequence number in a range has the string "E0" appended to it, which doesn't affect its numeric value, but gives you something to search for if you want to exclude the endpoint. You can exclude the beginning point by waiting for the sequence number to be greater than 1.

    You want a right side that does not succeed. Because once it succeeds, you start over on the left side again!

    Now a simple, correct match-always pattern is /^/ or /(?=)/, and a simple match-never pattern is /$^/ or /(?!)/. Indeed, using a match-never pattern:

    $ perl -le'for( qw( foo bar baz quux ), "" ){ print int scalar( /b/ .. + /$^/ ) }' 0 1 2 3 4 $ perl -le'for( qw( foo bar baz quux ), "" ){ print int scalar( /baz/ +.. /$^/ ) }' 0 0 1 2 3

    Well, that sequence looks right. So that means you can say:

    log_failure() unless grep { ( exists $hash{$_} and ( do_something($_), 1 ) ) .. /$^/ } @options;

    Makeshifts last the longest.