2011-07-05 21 views
8

el siguiente códigoPerl bucle foreach con cierre de función gobierna

#!/usr/bin/env perl 

use strict; 
use warnings; 

my @foo = (0,1,2,3,4); 

foreach my $i (@foo) { 
    sub printer { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    } 

    printer("test"); 
} 

no hace lo que se puede esperar.

¿Qué está pasando exactamente? (I esperaría que imprimir "test-0 \ NTest-1 \ NTest-2 \ NTest-3 \ NTest-4 \ n")

Respuesta

19

El problema es que el constructo sub name {...} no pueden anidarse como esa en un bucle for.

La razón es porque sub name {...} realmente significa BEGIN {*name = sub {...}} y los bloques de inicio se ejecutan tan pronto como se analizan. Por lo tanto, la compilación y el enlace variable de la subrutina ocurren en tiempo de compilación, antes de que el bucle for tenga la oportunidad de ejecutarse.

Lo que se quiere hacer es crear una subrutina anónima, que se unirá a sus variables en tiempo de ejecución:

#!/usr/bin/env perl 

use strict; 
use warnings; 

my @foo = (0,1,2,3,4); 

foreach my $i (@foo) { 
    my $printer = sub { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    }; 

    $printer->("test"); 
} 

que imprime

test-0 
test-1 
test-2 
test-3 
test-4 

Es de suponer que en su caso de uso real, estos cierres ser cargado en una matriz o hash para que se pueda acceder más tarde.

También puede usar bareword identificadores con los cierres, pero hay que hacer un poco de trabajo extra para asegurarse de que los nombres son visibles en tiempo de compilación:

BEGIN { 
    for my $color (qw(red blue green)) { 
     no strict 'refs'; 
     *$color = sub {"<font color='$color'>@_</font>"} 
    } 
} 

print "Throw the ", red 'ball'; # "Throw the <font color='red'>ball</font>" 
7
respuesta de

Eric Strom es correcta, y probablemente lo quería ver, pero no entra en los detalles de la encuadernación.

Una breve nota sobre la vida útil léxica: lexicals se crean en tiempo de compilación y están realmente disponibles, incluso antes de entrar en su ámbito, como muestra este ejemplo:

my $i; 
BEGIN { $i = 42 } 
print $i; 

A partir de entonces, cuando salen del ámbito de aplicación, se deje de estar disponible hasta la próxima vez que están en su alcance:

print i(); 
{ 
    my $i; 
    BEGIN { $i = 42 } 
    # in the scope of `my $i`, but doesn't actually 
    # refer to $i, so not a closure over it: 
    sub i { eval '$i' } 
} 
print i(); 

en su código, el cierre se une a la inicial léxica $i en tiempo de compilación. Sin embargo, los bucles foreach son un poco extraños; mientras que el my $i realmente crea un léxico, el bucle foreach no lo usa; en su lugar, lo alude a uno de los valores en bucle en cada iteración y luego lo restaura a su estado original después del bucle. Por lo tanto, su cierre es lo único que hace referencia al léxico original $i.

Una ligera variación muestra más complejidad:

foreach (@foo) { 
    my $i = $_; 
    sub printer { 
     my $blah = shift @_; 
     print "$blah-$i\n"; 
    } 

    printer("test"); 
} 

Aquí, el original $i se crea en tiempo de compilación y el cierre se une a ese; la primera iteración del ciclo lo establece, pero la segunda iteración del ciclo crea un nuevo $i no asociado con el cierre.

+0

muy interesante, gracias – Snark

Cuestiones relacionadas