Est-ce que l'opérateur flip-flop de Perl mis sur écoute? Il a l'état global, comment puis-je le réinitialiser?

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

Était-ce utile?

La solution

Quelqu'un peut-il clarifier ce que la question de la documentation est? Il indique clairement:

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

Il y a un certain flou là-bas sur ce que « chaque » signifie, mais je ne pense pas que la documentation serait bien servi par une explication complexe.

Notez que d'autres itérateurs de Perl (each ou glob contexte scalaire) peut conduire aux mêmes problèmes. Parce que l'état de each est lié à un hachage particulier, pas un peu de code particulier, each peut être remis à zéro en appelant (même dans un contexte vide) keys sur le hachage. Mais pour glob ou .., il n'y a pas de mécanisme de remise à zéro disponible sauf en appelant le iterator jusqu'à ce qu'il soit remis à zéro. Un échantillon bug 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: 

Pour le trop curieux, voici quelques exemples où même .. dans la source est différent .. opérateur:

fermetures distinctes:

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

Commenter la ligne de $x if 0 pour voir que les non-fermetures ont une seule opération .. partagée par tous les « copies », avec la sortie étant 12.

Sujets:

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

code threadé commence par quel que soit l'état du .. avait été avant la création de fil, mais les changements à son état dans le fil sont isolés d'affecter quoi que ce soit d'autre.

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

Chaque profondeur de récursivité est séparée opérateur ...

Autres conseils

L'astuce n'est pas utiliser le même bascule de sorte que vous avez pas d'état de s'inquiéter. Il suffit de faire une fonction de générateur pour vous donner un nouveau sous-programme avec une nouvelle bascule que vous utilisez une seule fois:

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

J'écris aussi à ce sujet dans Faire exclusive flip bascule opérateurs .

Le "opérateur de gamme" .. est documentée dans perlop sous la rubrique "Gamme opérateurs". En regardant à travers le doucmentation, il semble qu'il n'y a aucun moyen de réinitialiser l'état de l'opérateur ... Chaque instance de l'opérateur .. conserve son propre État, ce qui signifie qu'il n'y a aucun moyen de se référer à l'état d'un opérateur de .. particulier.

On dirait qu'il est conçu pour les scripts très petits tels que:

if (101 .. 200) { print; }

La documentation indique que c'est l'abréviation de

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

D'une certaine manière l'utilisation de $. est là implicite (toolic souligne dans un commentaire que c'est documenté aussi). L'idée semble être que cette boucle fonctionne une fois (jusqu'à $. == 200) dans une instance donnée de l'interpréteur Perl, et donc vous n'avez pas à vous soucier de la réinitialisation de l'état de la .. bascule.

Cet opérateur ne semble pas trop utile dans un contexte plus général réutilisable, pour les raisons que vous avez identifiés.

Une solution de contournement / pirater / tricher pour votre cas particulier est d'ajouter la valeur finale à votre tableau:

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

Cela garantit que l'ERS de l'opérateur de gamme finira par être vrai.

Bien sûr, cela est en aucun cas une solution générale.

A mon avis, ce comportement est clairement documenté. Si vous pouvez construire une explication claire, vous pouvez appliquer un patch à perlop.pod via perlbug.

J'ai trouvé ce problème, et pour autant que je sache, il n'y a aucun moyen de le réparer. Le résultat est - ne pas utiliser l'opérateur .. dans les fonctions, sauf si vous êtes sûr que vous quittez à l'état faux lorsque vous quittez la fonction, sinon la fonction peut retourner une sortie différente pour la même entrée (ou présentent un comportement différent pour la même entrée).

Chaque utilisation de l'opérateur .. maintient son propre état. Comme Alex Brown a dit, vous devez le laisser dans l'état faux lorsque vous quittez la fonction. Peut-être que vous pourriez faire quelque chose comme:

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top