Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Perl's flip-flop operator bugged? It has global state, how can I reset it?

I'm dismayed. OK, so this was probably the most fun Perl bug I've ever found. Even today I'm learning new stuff about Perl. Essentially, the flip-flop operator .. which returns false until the left-hand-side returns true, and then true until the right-hand-side returns false keep global state (or that is what I assume.)

Can I reset it (perhaps this would be a good addition to Perl 4-esque hardly ever used reset())? Or, is there no way to use this operator safely?

I also don't see this (the global context bit) documented anywhere in perldoc perlop is this a mistake?

Code

use feature ':5.10'; use strict; use warnings;  sub search {     my $arr = shift;     grep { !( /start/ .. /never_exist/ ) } @$arr; }  my @foo = qw/foo bar start baz end quz quz/; my @bar = qw/foo bar start baz end quz quz/;  say 'first shot - foo'; say for search \@foo;  say 'second shot - bar'; say for search \@bar; 

Spoiler

$ perl test.pl first shot foo bar second shot 
like image 210
NO WAR WITH RUSSIA Avatar asked Jan 26 '10 23:01

NO WAR WITH RUSSIA


1 Answers

Can someone clarify what the issue with the documentation is? It clearly indicates:

Each ".." operator maintains its own boolean state. 

There is some vagueness there about what "Each" means, but I don't think the documentation would be well served by a complex explanation.

Note that Perl's other iterators (each or scalar context glob) can lead to the same problems. Because the state for each is bound to a particular hash, not a particular bit of code,each can be reset by calling (even in void context) keys on the hash. But for glob or .., there is no reset mechanism available except by calling the iterator until it is reset. A sample glob bug:

sub globme {     print "globbing $_[0]:\n";     print "got: ".glob("{$_[0]}")."\n" for 1..2; } globme("a,b,c"); globme("d,e,f"); __END__ globbing a,b,c: got: a got: b globbing d,e,f: got: c Use of uninitialized value in concatenation (.) or string at - line 3. got:  

For the overly curious, here are some examples where the same .. in the source is a different .. operator:

Separate closures:

sub make_closure {     my $x;     return sub {         $x if 0;  # Look, ma, I'm a closure         scalar( $^O..!$^O ); # handy values of true..false that don't trigger ..'s implicit comparison to $.     } } print make_closure()->(), make_closure()->(); __END__ 11 

Comment out the $x if 0 line to see that non-closures have a single .. operation shared by all "copies", with the output being 12.

Threads:

use threads; sub coderef { sub { scalar( $^O..!$^O ) } } coderef()->(); print threads->create( coderef() )->join(), threads->create( coderef() )->join(); __END__ 22 

Threaded code starts with whatever the state of the .. had been before thread creation, but changes to its state in the thread are isolated from affecting anything else.

Recursion:

sub flopme {     my $recurse = $_[0];     flopme($recurse-1) if $recurse;     print " "x$recurse, scalar( $^O..!$^O ), "\n";     flopme($recurse-1) if $recurse; } flopme(2) __END__ 1  1 2   1 3  2 4 

Each depth of recursion is a separate .. operator.

like image 148
ysth Avatar answered Sep 23 '22 00:09

ysth