28 votes

L'opérateur flip-flop de Perl est-il bogué ? Il a un état global, comment puis-je le réinitialiser ?

Je suis consterné. OK, il s'agit probablement de l'événement le plus important de l'histoire de l'Europe. amusant Perl que j'ai jamais trouvée. Aujourd'hui encore, j'apprends de nouvelles choses sur Perl. Essentiellement, l'opérateur flip-flop .. qui renvoie faux jusqu'à ce que le côté gauche renvoie vrai et ensuite vrai jusqu'à ce que le côté droit retourne faux conserver l'état global (c'est du moins ce que je suppose).

Puis-je le réinitialiser (ce serait peut-être un bon ajout à Perl 4-esque qui n'est pratiquement jamais utilisé) ? reset() ) ? Ou n'y a-t-il pas moyen d'utiliser cet opérateur en toute sécurité ?

Je ne vois pas non plus que cela (le contexte global) soit documenté quelque part dans le document perldoc perlop s'agit-il d'une erreur ?

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;

Filigrane

$ perl test.pl
first shot
foo
bar
second shot

33voto

ysth Points 54757

Quelqu'un peut-il préciser quel est le problème avec la documentation ? Elle indique clairement :

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

Il y a un certain flou quant à la signification de "Chaque", mais je ne pense pas que la documentation serait bien servie par une explication complexe.

Notez que les autres itérateurs de Perl ( each ou contexte scalaire glob ) peut entraîner les mêmes problèmes. Parce que l'état de each est lié à un hachage particulier, et non à un bout de code particulier, each peut être réinitialisé en appelant (même dans un contexte vide) keys sur le hachage. Mais pour les glob o .. il n'existe aucun mécanisme de réinitialisation, si ce n'est en appelant l'itérateur jusqu'à ce qu'il soit réinitialisé. Un exemple de bogue de 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 les plus curieux, voici quelques exemples où le même .. dans la source est un différents opérateur :

Fermetures séparées :

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

Commentez le $x if 0 pour voir que les non-closures ont une seule opération partagée par toutes les "copies", la sortie étant 12 .

Fils :

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

Le code threadé démarre avec l'état du avant la création du thread, mais les modifications de son état dans le thread sont isolées et n'affectent rien d'autre.

La récursivité :

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écursion est un opérateur distinct.

18voto

brian d foy Points 71781

L'astuce consiste à ne pas utiliser les mêmes bascule Vous n'avez donc pas à vous préoccuper de l'État. Il suffit de créer une fonction générateur pour obtenir un nouveau sous-programme avec une nouvelle bascule que vous n'utiliserez qu'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'en parle également dans Faire des opérateurs de bascule exclusifs .

7voto

Greg Hewgill Points 356191

L'"opérateur de gamme" .. est documenté dans perlop sous "Range Operators". En consultant la documentation, il semble qu'il n'y ait aucun moyen de réinitialiser l'état de l'opérateur de plage. .. de l'opérateur. Chaque instance de l'opérateur .. L'opérateur conserve son propre ce qui signifie qu'il n'y a aucun moyen de se référer à l'état d'un .. de l'opérateur.

Il semble qu'il soit conçu pour de très petits scripts tels que :

if (101 .. 200) { print; }

La documentation indique qu'il s'agit d'une abréviation pour

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

D'une manière ou d'une autre, l'utilisation de $. est implicite (toolic signale dans un commentaire que c'est également documenté). L'idée semble être que cette boucle exécute une fois (jusqu'à $. == 200 ) dans une instance donnée de l'interpréteur Perl, et vous n'avez donc pas besoin de vous préoccuper de réinitialiser l'état de l'élément .. bascule.

Cet opérateur ne semble pas très utile dans un contexte plus général de réutilisation, pour les raisons que vous avez identifiées.

7voto

toolic Points 23449

Une solution de contournement, de piratage ou de triche pour votre cas particulier consiste à ajouter la valeur finale à votre tableau :

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

Cela garantira que l'opérateur RHS de l'intervalle sera finalement vrai.

Bien entendu, il ne s'agit en aucun cas d'une solution générale.

À mon avis, ce comportement n'est pas clairement documenté. Si vous pouvez construire une explication claire, vous pourriez appliquer un correctif à perlop.pod via perlbug .

2voto

Alex Brown Points 15776

J'ai trouvé ce problème et, pour autant que je sache, il n'y a aucun moyen de le résoudre. Le résultat est le suivant : n'utilisez pas la fonction .. dans les fonctions, à moins que vous ne soyez sûr de le laisser dans l'état faux lorsque vous quittez la fonction, sinon la fonction peut renvoyer une sortie différente pour la même entrée (ou présenter un comportement différent pour la même entrée).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X