2011-01-12 20 views
17

Una de las primeras cosas que intento aprender en un lenguaje de programación desconocido es cómo maneja los cierres. Su semántica a menudo se entrelaza con la forma en que el lenguaje maneja los ámbitos y otras partes difíciles, por lo que su comprensión revela varios otros aspectos del lenguaje. Además, los cierres son una construcción realmente poderosa y muchas veces reducen la cantidad de repetición que tengo que escribir. Así que me estaba metiendo con cierres de Perl y me encontré con un poco de Gotcha:

my @closures; 
foreach (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 

Cuando escribí el código anterior que estaba esperando para ver

I will remember 1 
I will remember 2 
I will remember 3 

pero en cambio me dieron I will remember CODE(0x986c1f0).

El experimento anterior reveló que $_ es muy dependiente del contexto y si aparece en un cierre, entonces su valor no se fija en el momento de la creación del cierre. Se comporta más como una referencia. ¿Qué otras trampas debo tener en cuenta al crear cierres en Perl?

Respuesta

25

Los cierres solo se cierran sobre las variables léxicas; $_ es normalmente una variable global. En 5.10 y superior, puede decir my $_; para que sea léxico en un ámbito determinado (aunque en 5.18 esto se declaró retroactivamente como experimental y sujeto a cambios, por lo que es mejor usar algún otro nombre de variable).

Esto produce la salida que esperaba:

use strict; 
use warnings; 
use 5.010; 
my @closures; 
foreach my $_ (1..3) { 
    # create some closures 
    push @closures, sub { say "I will remember $_"; }; 
} 
foreach (@closures) { 
    # call the closures to see what they remember 
    # the result is not obvious 
    &{$_}(); 
} 
+2

Pero esto es prácticamente lo mismo que usar 'foreach my $ num ...'. – davidk01

+1

Entonces, si solo cierran sobre variables léxicas, ¿qué ocurre con otros tipos de variables? ¿Utiliza el cierre el valor disponible durante el sitio de llamada como lo hace para '$ _'? – davidk01

+1

@ davidk01: como en cualquier otro lenguaje de programación que permita variables globales, los cierres realmente se cierran sobre esa variable como normal. Acaba de olvidar que es global y, por lo tanto, puede cambiar cuando otro código establece su valor en otra cosa (como por el propio sub anónimo cuando se llama). – slebetman

3

$ _ es una variable global y no debe ser utilizado en el cierre. Antes de usarlo, asígnelo a una variable de ámbito léxico como se muestra abajo. Esto producirá o/p esperado.

#!/usr/bin/perl -w 

use strict; 
my @closures; 


foreach (1..3) { 
    my $var = $_; 
    push @closures, sub { print "I will remember $var"; }; 
} 

foreach (@closures) { 
    $_->(); 
    print "\n"; 
}