Domanda

Io sono costernato. OK, quindi questo è stato probabilmente il più divertente Perl il bug l'ho mai trovato.Anche io oggi sto imparando cose nuove su Perl.Essenzialmente, il flip-flop operatore .. che restituisce false fino a quando la sinistra restituisce vero, e quindi vero fino a quando il lato restituisce false mantenere lo stato globale (o che è quello che io suppongo.)

Posso resettarlo (forse questa sarebbe una buona aggiunta a Perl 4-esque quasi mai usato reset())?O, non c'è modo di utilizzare questo operatore in modo sicuro?

Anche io non vedo questo (il contesto globale bit) documentato in perldoc perlop si tratta di un errore?

Codice

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
È stato utile?

Soluzione

Qualcuno può chiarire qual è il problema con la documentazione è? Esso indica chiaramente:

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

V'è una certa imprecisione c'è su ciò che "ogni" mezzo, ma non credo che la documentazione sarebbe ben servito da una spiegazione complessa.

Si noti che gli altri iteratori di Perl (each o scalare glob contesto) può portare agli stessi problemi. Perché lo stato per each è legato a un particolare hash, non una particolare porzione di codice, each può essere azzerato chiamando (anche in un contesto vuoto) keys sul hash. Ma per glob o .., non v'è alcun meccanismo di ripristino disponibili ad eccezione chiamando l'iteratore fino al reset. Un bug di esempio glob:

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: 

Per il troppo curiosi, ecco alcuni esempi in cui lo stesso .. nella fonte è un diverso .. operator:

chiusure separati:

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

commento la riga $x if 0 di vedere che non le chiusure hanno un unico .. operazione condivisa da tutte le "copie", con l'uscita di essere 12.

Discussioni:

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

codice threaded inizia con qualunque sia lo stato della .. era stato prima della creazione thread, ma le modifiche allo stato nel thread sono isolati da qualsiasi altra cosa che colpisce.

La ricorsione:

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

Ogni profondità di ricorsione è un operatore di .. a parte.

Altri suggerimenti

Il trucco non è utilizzare lo stesso flip-flop in modo da avere nessuno stato di cui preoccuparsi. Basta fare una funzione di generatore per dare una nuova subroutine con un nuovo flip-flop di utilizzare solo una volta:

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";

Scrivo anche di questo in Fai flip esclusiva flop di operatori .

Il "operatore di intervallo" .. è documentato in perlop sotto "gli Operatori di Intervallo".Guardando attraverso il doucmentation, sembra che non vi è alcun modo per ripristinare lo stato dei .. operatore.Ogni istanza del .. l'operatore mantiene la sua proprio stato, il che significa che non vi è alcun modo per vedere lo stato di ogni particolare .. operatore.

Sembra che è stato progettato per i piccoli script, ad esempio:

if (101 .. 200) { print; }

La documentazione indica che questo è breve per

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

In qualche modo l'uso di $. è implicito che c' (toolic sottolinea in un commento che è documentato troppo).L'idea sembra essere che questo ciclo viene eseguito una volta (fino a $. == 200) in una determinata istanza dell'interprete Perl, e quindi non è necessario preoccuparsi di ripristino dello stato dei .. flip-flop.

Questo operatore non sembrare troppo utile in un più generale riutilizzabile contesto, per i motivi che avete identificato.

Una soluzione / hack / imbrogliare per il vostro caso particolare è quello di aggiungere il valore finale per l'array:

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

In questo modo garantisce che il RHS dell'operatore gamma finirà per essere vero.

Naturalmente, questo non è in alcun modo una soluzione generale.

A mio parere, questo comportamento non è chiaramente documentato. Se si riesce a costruire una spiegazione chiara, è possibile applicare una patch per perlop.pod via perlbug.

Ho trovato questo problema, e per quanto ne so non c'è modo per risolvere il problema. Il risultato è - non utilizzare l'operatore .. nelle funzioni, a meno che non si è sicuri si sta lasciando nello stato falso quando si lascia la funzione, altrimenti la funzione può restituire output diverso per lo stesso ingresso (o un comportamento diverso per la stesso input).

Ogni utilizzo dell'operatore .. mantiene il suo proprio stato. Come Alex Brown ha detto, è necessario lasciare in stato di false quando si lascia la funzione. Forse si potrebbe fare qualcosa di simile:

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top