2012-01-02 24 views
10

me gustaría utilizar $a y $b las variables en mis funciones binarias anónimos como se hace en sort {$a <=> $b} (1, 2, 3) pero no puedo entender por qué código como

#!/usr/bin/env perl 
use strict; 
use warnings; 

Foo::Bar(sub { $a + $b }); 

package Foo; 
sub Bar { 
    my ($function) = @_; 

    for my $i (1, 2, 3) { 
     local ($a, $b) = ($i, $i); 
     print $function->() . "\n"; 
    } 
}  

No funciona. Mientras que

#!/usr/bin/env perl 
use strict; 
use warnings; 

Foo::Bar(sub { $_ }); 

package Foo; 
sub Bar { 
    my ($function) = @_; 

    for my $i (1, 2, 3) { 
     local $_ = $i; 
     print $function->() . "\n"; 
    } 
} 

funciona bien.

¿Qué estoy haciendo mal?

Respuesta

14

$a y $b son variables especiales del paquete. Está llamando al Foo::Bar desde su paquete main, por lo que necesita establecer $main::a y $main::b para que funcione. Puede usar caller para obtener el nombre del paquete que realiza la llamada. Esto debería funcionar:

#!/usr/bin/env perl 
use strict; 
use warnings; 

Foo::Bar(sub { $a + $b }); 

package Foo; 
sub Bar { 
    my ($function) = @_; 
    my $pkg = caller; 

    for my $i (1, 2, 3) { 
     no strict 'refs'; 
     local *{ $pkg . '::a' } = \$i; 
     local *{ $pkg . '::b' } = \$i; 
     print $function->() . "\n"; 
    } 
}  
+9

+1. Creo que esta es una práctica extremadamente mala sin embargo. – flesk

+1

en mi humilde opinión, está bien si desea crear una función tipo 'ordenar' que toma un bloque; pero esa función solo debería devolver valores y no 'imprimir', obviamente. Esto está bien como una demostración sin embargo. – friedo

+5

Eso es verdad. Sospecho que el OP no sabe realmente lo que está haciendo, y estaría mejor con 'sub {$ _ [0] + $ _ [1]}' y '$ function -> ($ i, $ i)' . – flesk

0

Por si acaso alguien está interesado, una copia y pegar desde List::MoreUtils::PP v.0.428 (a diciembre de 2017):

# begin copyrighted content 
sub reduce_u(&@) 
{ 
    my $code = shift; 

    # Localise $a, $b 
    my ($caller_a, $caller_b) = do 
    { 
     my $pkg = caller(); 
     no strict 'refs'; 
     \*{$pkg . '::a'}, \*{$pkg . '::b'}; 
    }; 

    local (*$caller_a, *$caller_b); 
    *$caller_a = \(); 
    for (0 .. $#_) 
    { 
     *$caller_b = \$_[$_]; 
     *$caller_a = \($code->()); 
    } 

    ${*$caller_a}; 
} 
# end copyrighted content 

Sólo se diferencia del ejemplo anterior en el área afectada por no strict 'refs';. Probablemente no se puede hacer sin ningún estricto en absoluto.

Cuestiones relacionadas