Ist Perls Flip-Flop-Bedienerbetreiber stört? Es hat einen globalen Staat, wie kann ich es zurücksetzen?

StackOverflow https://stackoverflow.com/questions/2143554

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
War es hilfreich?

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");
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top