Pregunta

Estoy consternado. OK, así que esto fue probablemente el más divertido Perl bug que he encontrado nunca.Incluso hoy en día estoy aprendiendo cosas nuevas acerca de Perl.Esencialmente, el flip-flop operador .. que devuelve falso hasta que el lado izquierdo devuelve cierto, y , a continuación, cierto hasta que el lado derecho de devoluciones falso mantener el estado global (o eso es lo que supongo.)

Puedo resetearlo (tal vez esta sería una buena adición a Perl 4-esque casi no se usa reset())?O, no hay manera de utilizar este operador de forma segura?

Yo también no ver esto (el contexto global de bits) que se documenta en cualquier lugar 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
¿Fue útil?

Solución

Puede alguien aclarar cuál es el problema con la documentación es? Se indica claramente:

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

No hay una cierta ambigüedad sobre lo que "cada" medios, pero no creo que la documentación sería bien servido por una explicación compleja.

Tenga en cuenta que otros iteradores de Perl (each o glob contexto escalar) pueden dar lugar a los mismos problemas. Debido a que el estado de each se une a un hash en particular, no es un poco de código en particular, each se puede restablecer llamando al (incluso en el contexto void) keys en el hash. Pero para glob o .., no existe un mecanismo de reposición disponibles excepto llamando al repetidor hasta que se restablezca. Un error pegote de ejemplo:

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 el excesivamente curioso, he aquí algunos ejemplos en los que el mismo .. en la fuente es un diferente .. operador:

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 ver que los cierres no tienen una sola operación .. compartida por todas las "copias", con la salida siendo 12.

Temas:

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

código roscados se inicia con cualquiera que sea el estado de la .. había sido antes de la creación del hilo, pero los cambios en su estado en el hilo están aislados de cualquier otra cosa que afecta.

recursión:

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 separado.

Otros consejos

El truco no se utilice la misma flip-flop por lo que tiene ningún estado que preocuparse. Simplemente haga una función de generador para darle un nuevo subprograma con un nuevo flip-flop que sólo utilice 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 escribir sobre esto en Hacer flip-exclusiva el flop operadores .

El .. "operador de rango" está documentada en perlop en "Operadores Rango". Mirando a través de la doucmentation, parece que no hay ninguna manera de restablecer el estado del operador ... Cada instancia del operador .. mantiene su propia estado, lo que significa que no hay ninguna manera de referirse al estado de cualquier operador .. particular.

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

if (101 .. 200) { print; }

La documentación que esta es la abreviatura de

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

De alguna manera el uso de $. está implícito allí (puntos toolic a cabo en un comentario que eso está documentado también). La idea parece ser que este bucle se ejecuta una vez (hasta $. == 200) en un caso dado de la intérprete de Perl, y por lo tanto usted no necesita preocuparse acerca de cómo restablecer el estado de la .. flip-flop.

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

Una solución / truco / trucos para su caso particular es anexar el valor final de la matriz:

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

Esto garantiza que el lado derecho del operador de rango con el tiempo será verdadera.

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

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

Me encontré con este problema, y que yo sepa no hay manera de solucionarlo.El resultado de todo esto es - no utilizar el .. operador en funciones, a menos que estés seguro de que estás dejando en el falso estado al salir de la función, de lo contrario, la función puede devolver diferentes de salida para la misma entrada (o presentan un comportamiento diferente para la misma entrada).

Cada uso del operador .. mantiene su propio estado. Al igual que Alex Brown, dijo, es necesario dejarlo en el estado falso cuando salga de la función. Tal vez usted podría 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");
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top