perl operators global-state flip-flop

¿El operador de flip-flop de Perl está en modo bug? Tiene estado global, ¿cómo puedo restablecerlo?



operators global-state (6)

¿Alguien puede aclarar cuál es el problema con la documentación? Indica claramente:

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

Hay algo de imprecisa en lo que se refiere a "lo que significa" cada uno, pero no creo que la documentación esté bien atendida por una explicación compleja.

Tenga en cuenta que los otros iteradores de Perl ( each o un glob contexto escalar) pueden provocar los mismos problemas. Debido a que el estado de each está ligado a un hash en particular, no a un bit de código en particular, each puede restablecerse llamando (incluso en el contexto vacío) las keys del hash. Pero para glob o .. , no hay ningún mecanismo de reinicio disponible excepto llamando al iterador hasta que se restablezca. Una muestra de error global:

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:

Para los más curiosos, aquí hay algunos ejemplos donde lo mismo ... en la fuente es un operador diferente ..

Cierres separados:

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

Comente la línea $x if 0 para ver que los no cierres tengan una sola operación ... compartida por todas las "copias", con el resultado 12 .

Trapos:

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

El código enhebrado comienza con cualquiera que sea el estado de la ... antes de la creación del hilo, pero los cambios a su estado en el hilo están aislados de afectar a cualquier otra cosa.

Recursividad:

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

Cada profundidad de recursión es un operador por separado.

Estoy consternado. OK, este fue probablemente el error Perl más divertido que he encontrado. Incluso hoy estoy aprendiendo cosas nuevas sobre Perl. Básicamente, el operador de flip-flop .. que devuelve falso hasta que el lado izquierdo devuelve verdadero , y luego verdadero hasta que el lado derecho devuelve falso mantenga el estado global (o eso es lo que supongo).

¿Puedo restablecerlo (quizás esto sería una buena adición a Perl 4-esque casi nunca reset() usado reset() )? O bien, ¿no hay forma de usar este operador de forma segura?

Tampoco veo esto (el bit de contexto global) documentado en ningún lugar en perldoc perlop es esto un error?

Código

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


Cada uso del operador .. mantiene su propio estado. Como dijo Alex Brown, debes dejarlo en el estado falso cuando salgas de la función. Tal vez podrías hacer algo como:

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


El "operador de rango" está documentado en perlop bajo "Operadores de rango". Al mirar a través de la documentación, parece que no hay forma de restablecer el estado del operador .. Cada instancia del operador .. mantiene su propio estado, lo que significa que no hay forma de referirse al estado de ningún operador en particular.

Parece que está diseñado para scripts muy pequeños como:

if (101 .. 200) { print; }

La documentación indica que esto es una abreviatura de

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

De alguna manera, el uso de $. está implícito allí (la herramienta señala en un comentario que eso también está documentado). La idea parece ser que este ciclo se ejecuta una vez (hasta $. == 200 ) en una instancia determinada del intérprete Perl, y por lo tanto no necesita preocuparse por restablecer el estado del flip-flop.

Este operador no parece demasiado útil en un contexto reutilizable más general, por las razones que ha identificado.


El truco es no utilizar el mismo flip-flop por lo que no tienes que preocuparte por ningún estado. Simplemente haga una función de generador para darle una nueva subrutina con un nuevo flip-flop que solo use una vez:

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

También escribo sobre esto en Hacer operadores de flip-flop exclusivos .


Encontré este problema y, hasta donde sé, no hay forma de solucionarlo. El resultado es - no use el operador .. en las funciones, a menos que esté seguro de que lo está dejando en el estado falso cuando abandona la función, de lo contrario la función puede devolver resultados diferentes para la misma entrada (o mostrar un comportamiento diferente para la misma entrada).


Una solución alternativa / truco / truco para su caso particular es agregar el valor final a su matriz:

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

Esto garantizará que el RHS del operador de rango finalmente sea cierto.

Por supuesto, esto no es de ninguna manera una solución general.

En mi opinión, este comportamiento no está claramente documentado. Si puede construir una explicación clara, puede aplicar un parche a perlop.pod través de perlbug .