Ist Perls Flip-Flop-Bedienerbetreiber stört? Es hat einen globalen Staat, wie kann ich es zurücksetzen?
-
23-09-2019 - |
Frage
Ich bin bestürzt. OK, das war wahrscheinlich am meisten Spaß Perl Fehler, den ich jemals gefunden habe. Noch heute lerne ich neue Sachen über Perl. Im Wesentlichen der Flip-Flop-Operator ..
was kehrt zurück FALSCH bis die linke Seite zurückkehrt Stimmt, und dann Stimmt Bis die rechte Seite zurückkehrt FALSCH Halten Sie den globalen Zustand (oder das nehme ich an.)
Kann ich es zurücksetzen? reset()
)? Oder gibt es keine Möglichkeit, diesen Bediener sicher zu verwenden?
Ich sehe diesen (der globale Kontextbit) auch nirgendwo dokumentiert perldoc perlop
Ist das ein Fehler?
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
Lösung
Kann jemand klarstellen, was das Problem mit der Dokumentation ist? Es zeigt deutlich:
Each ".." operator maintains its own boolean state.
Es gibt einige Unbestimmtheit darüber, was "jeder" bedeutet, aber ich glaube nicht, dass die Dokumentation durch eine komplexe Erklärung gut bedient wird.
Beachten Sie, dass Perls andere Iteratoren (each
oder skalarer Kontext glob
) kann zu den gleichen Problemen führen. Weil der Staat für each
ist an einen bestimmten Hash gebunden, kein bestimmtes Stück Code,each
kann durch Anruf zurückgesetzt werden (auch im void -Kontext) keys
auf dem Hash. Aber für glob
oder ..
, Es gibt keinen Reset -Mechanismus, außer wenn er den Iterator aufruft, bis er zurückgesetzt wird. Ein Beispiel GLOB -Fehler:
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:
Für die übermäßig Neugierigen hier sind einige Beispiele, bei denen dieselbe .. in der Quelle ist a anders .. Operator:
Getrennte Schließungen:
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
Kommentieren Sie die $x if 0
Zeile, um zu sehen, dass Nichtschlüsse eine einzelne ... von allen "Kopien" geteilte Operation haben, wobei die Ausgabe ist 12
.
Themen:
use threads;
sub coderef { sub { scalar( $^O..!$^O ) } }
coderef()->();
print threads->create( coderef() )->join(), threads->create( coderef() )->join();
__END__
22
Der Thread -Code beginnt mit dem Zustand der .. vor der Erstellung von Thread, aber Änderungen in seinem Zustand im Thread sind isoliert, wenn sie etwas anderes beeinflussen.
Rekursion:
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
Jede Tiefe der Rekursion ist ein separater. Operator.
Andere Tipps
Der Trick ist nicht dasselbe verwendet Flip Flops Sie haben also keinen Zustand, über den Sie sich Sorgen machen müssen. Machen Sie einfach eine Generatorfunktion, um Ihnen ein neues Unterprogramm mit einem neuen Flip-Flop zu geben, den Sie nur einmal verwenden:
sub make_search {
my( $left, $right ) = @_;
sub {
grep { !( /\Q$left\E/ .. /\Q$right\E/ ) } @{$_[0]};
}
}
my $search_sub1 = make_search( 'start', 'never_existed' );
my $search_sub2 = make_search( 'start', 'never_existed' );
my @foo = qw/foo bar start baz end quz quz/;
my $count1 = $search_sub1->( \@foo );
my $count2 = $search_sub2->( \@foo );
print "count1 $count1 and count2 $count2\n";
Ich schreibe auch darüber Machen Sie exklusive Flip-Flop-Operatoren.
Der "Range Operator" ..
ist dokumentiert in Perlop unter "Range -Operatoren". Wenn man die Doppelblätterung durchschaut, scheint es keine Möglichkeit zu haben, den Zustand der zurückzusetzen ..
Operator. Jede Instanz der ..
Bediener behält seine besitzen Staat, was bedeutet, dass es keine Möglichkeit gibt, sich auf den Zustand eines bestimmten zu beziehen ..
Operator.
Es sieht so aus, als wäre es für sehr kleine Skripte konzipiert wie:
if (101 .. 200) { print; }
Die Dokumentation besagt, dass dies kurz ist für
if ($. == 101 .. $. == 200) { print; }
Irgendwie die Verwendung von $.
ist dort implizit (Toolic weist in einem Kommentar darauf hin, dass dies auch dokumentiert ist). Die Idee scheint zu sein, dass diese Schleife läuft einmal (bis um $. == 200
) In einem bestimmten Fall des Perl -Dolmetschers, und daher müssen Sie sich keine Sorgen machen, den Zustand der zurückzusetzen ..
Flip Flops.
Dieser Betreiber scheint in einem allgemeineren wiederverwendbaren Kontext nicht allzu nützlich zu sein, aus den von Ihnen identifizierten Gründen.
Eine Problemumgehung/Hack/Cheat für Ihren speziellen Fall besteht darin, den Endwert Ihrem Array anzuhängen:
sub search {
my $arr = shift;
grep { !( /start/ .. /never_exist/ ) } @$arr, 'never_exist';
}
Dies wird garantieren, dass der RH des Range -Operators irgendwann wahr sein wird.
Dies ist natürlich in keiner Weise eine allgemeine Lösung.
Meiner Meinung nach ist dieses Verhalten nicht klar dokumentiert. Wenn Sie eine klare Erklärung erstellen können, können Sie einen Patch auf einen Patch anwenden perlop.pod
über perlbug
.
Ich fand dieses Problem und soweit ich weiß, gibt es keine Möglichkeit, es zu beheben. Das Ende ist - benutze die nicht ..
Operator in Funktionen, es sei denn, Sie sind sicher, dass Sie es im falschen Zustand lassen, wenn Sie die Funktion verlassen, andernfalls kann die Funktion eine andere Ausgabe für denselben Eingang zurückgeben (oder ein anderes Verhalten für dieselbe Eingabe aufweisen).
Jede Verwendung des ..
Der Betreiber unterhält seinen eigenen Staat. Wie Alex Brown sagte, müssen Sie es im falschen Zustand lassen, wenn Sie die Funktion verlassen. Vielleicht könntest du so etwas tun wie:
sub search {
my $arr = shift;
grep { !( /start/ || $_ eq "my magic reset string" ..
/never_exist/ || $_ eq "my magic reset string" ) }
(@$arr, "my magic reset string");
}