2010-01-26 5 views
29

Ich bin bestürzt. OK, also das war wahrscheinlich am meisten SpaßPerl Bug, den ich je gefunden habe. Sogar heute lerne ich neue Dinge über Perl. Im Wesentlichen wird der Flip-Flop-Operator .. die zurück falsch, bis die linke-Seite zurück wahr und dann wahr, bis die rechte Seite zurück falsch globalen Zustand halten (oder das ist, was ich angenommen.)Ist Perls Flip-Flop-Operator abgehört? Es hat einen globalen Status, wie kann ich es zurücksetzen?

Kann ich es zurücksetzen (vielleicht wäre das eine gute Ergänzung zu Perl   4-esque kaum jemals verwendet reset())? Oder gibt es keine Möglichkeit, diesen Operator sicher zu verwenden?

Ich sehe das auch nicht (das globale Kontext Bit) dokumentiert irgendwo in 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 
+2

Gute Frage! Sieht so aus, als würde es sich wie eine Schließung verhalten. – Demosthenex

+0

Oh, ich traf dies - ich schrieb eine Funktion mit dem Flipflop, rief es zweimal und das zweite Mal hatte es den Zustand von der ersten erhalten. meine App kaputt gemacht! –

+2

Wow, ich wusste nie, dass 'reset' existiert hat. ++ nur dafür. :) – friedo

Antwort

34

Kann jemand klären, was das Problem mit der Dokumentation ist? Es zeigt deutlich:

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

Es gibt einige Unklarheit gibt es über das, was „Jeder“ bedeutet, aber ich glaube nicht, die Dokumentation würde auch durch eine komplexe Erklärung serviert werden.

Beachten Sie, dass Perls andere Iteratoren (each oder skalare Kontext glob) zu den gleichen Problemen führen können. Da der Status für each an einen bestimmten Hash gebunden ist, nicht an ein bestimmtes Codebit, kann each durch Aufrufen (auch im leeren Kontext) keys auf dem Hash zurückgesetzt werden. Für glob oder .. ist jedoch kein Rücksetzungsmechanismus verfügbar, außer der Iterator wird aufgerufen, bis er zurückgesetzt wird. Eine Probe 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 allzu neugierig, hier sind einige Beispiele, bei denen das gleiche .. in der Quelle ein verschiedenen ist .. Betreiber:

separate Verschlüsse:

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 Nicht-Schließungen eine einzige Operation haben, die von allen "Kopien" geteilt wird, wobei die Ausgabe 12 ist.

Themen:

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

Threaded Code beginnt mit dem, was dem Zustand des .. hatte vor Thread-Erzeugung, aber Änderungen an seinen Zustand im Thread sind isoliert von alles andere zu 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 Rekursionstiefen ist ein separater .. Operator.

+2

+1 - Wie die Idee, separate Verschlüsse zu verwenden. – mob

+1

ysth ++ Ihre Antwort ist schön und großartig. Genau das habe ich gesucht. Vielen Dank. –

7

Die "Bereichsoperator" .. in perlop unter "Range-Operatoren" dokumentiert. Beim Durchsehen der Anordnung scheint es, dass es keine Möglichkeit gibt, den Zustand des .. Operators zurückzusetzen. Jede Instanz des ..-Bedieners behält seinen eigenen-Zustand bei, was bedeutet, dass es keine Möglichkeit gibt, sich auf den Zustand eines bestimmten ..-Bedieners zu beziehen.

Es sieht aus wie es für sehr kleine Skripte wie fertigt:

if (101 .. 200) { print; } 

Die Dokumentation besagt, dass dies für

if ($. == 101 .. $. == 200) { print; } 

kurz Irgendwie ist der Einsatz von $. implizit ist es (toolic Punkte aus in einem Kommentar, der auch dokumentiert ist). Die Idee scheint zu sein, dass diese Schleife einmal (bis $. == 200) in einer bestimmten Instanz des Perl-Interpreters ausgeführt wird, und deshalb müssen Sie sich keine Sorgen über das Zurücksetzen des Status des ..-Flip-Flops machen.

Dieser Operator scheint aus den von Ihnen angegebenen Gründen in einem allgemeineren wiederverwendbaren Kontext nicht allzu nützlich zu sein.

+0

Ein Zitat aus der Dokumentation: "Wenn einer der Operanden von Skalar .. ein konstanter Ausdruck ist, wird dieser Operand als wahr betrachtet, wenn er gleich (==) mit der aktuellen Eingabezeilennummer ist (die $. Variable)." 101 und 202 sind beide konstante Ausdrücke. – toolic

+0

Richtig, ich habe nicht in den Dokumenten gesehen '', wo der globale Kontext des Betreibers erwähnt wurde '' - während ich "perldoc perlop" erwähnte, qualifizierte ich die Doc-Frage nicht genug Entschuldigung für die Verwirrung. –

2

Ich habe dieses Problem gefunden, und soweit ich weiß, gibt es keine Möglichkeit, es zu beheben. Das Ergebnis ist - verwenden Sie nicht den .. Operator in Funktionen, außer Sie sind sicher, dass Sie ihn im falschen Zustand belassen, wenn Sie die Funktion verlassen, sonst kann die Funktion einen anderen Ausgang für die gleiche Eingabe zurückgeben (oder ein anderes Verhalten für die gleicher Eingang).

+0

Dies sollte wahrscheinlich in der Dokumentation deutlich gemacht werden. * pokes brian d foy * – Ether

+0

Ich kann morgen einen doc-Patch einreichen, aber es scheint, als wäre es besser behoben worden, indem man perl gepuffert hat, um das '..' nicht so global zu machen und möglicherweise mit' reset() ' –

+1

I fügte eine kurze Anmerkung zu perlop hinzu, aber ich dachte es war vorher klar. Das Problem tritt immer dann auf, wenn man Dinge liest, die nicht da sind, wie wenn man davon ausgeht, dass es nichts ist, wenn es nicht heißt. –

1

Jede Verwendung des .. Operators behält seinen eigenen Status bei. Wie Alex Brown sagte, müssen Sie es in den falschen Zustand verlassen, wenn Sie die Funktion verlassen.Vielleicht könnten Sie so etwas wie tun:

sub search { 
    my $arr = shift; 
    grep { !(/start/ || $_ eq "my magic reset string" .. 
      /never_exist/ || $_ eq "my magic reset string") } 
     (@$arr, "my magic reset string"); 
} 
+0

Ich würde das Wort 'use' nicht verwenden. Ich denke, es ist genauer zu sagen "Aussehen in der Quelle" –

+2

@Evan Carroll: siehe meine Antwort für, wo das ungenau ist. – ysth

+0

Yep yep, du bist offensichtlich der bessere perl'er tolle Post. (Ich hätte die Threads erraten) –

7

Eine Abhilfe/Hack/für Ihren speziellen Fall betrügen ist das Ende den Wert Ihrer Array anhängen:

sub search { 
    my $arr = shift; 
    grep { !(/start/ .. /never_exist/) } @$arr, 'never_exist'; 
} 

Dadurch wird gewährleistet, dass die rechte Seite von Bereichsoperator wird irgendwann wahr sein.

Natürlich ist dies in keiner Weise eine allgemeine Lösung.

Meiner Meinung nach ist dieses Verhalten nicht eindeutig dokumentiert. Wenn Sie eine klare Erklärung erstellen können, können Sie einen Patch auf perlop.pod über perlbug anwenden.

+0

Dies ist natürlich eine einfache gültige Workaround (eine praktikable Lösung), und ich habe alle Antworten auf diese Frage abgestimmt. Aber momentan suche ich mehr über den Operator. –

+3

Oder schieben Sie es nicht zuerst auf das Array: 'grep {! (/ Start/../never_exist /)} (@ $ arr, 'niemals_exist');' –

+0

@ brian/Dave: Ja, ich war sorglos. Ich habe den Code aktualisiert. – toolic

18

Der Trick ist nicht die gleiche flip-flop verwenden, so dass Sie keinen Staat haben, über den Sie sich Sorgen machen müssen. Machen Sie einfach eine Generatorfunktion geben Ihnen ein neues Unterprogramm mit einem neuen Flip-Flop, die 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 auch darüber in Make exclusive flip-flop operators schreiben.

Verwandte Themen