2012-05-08 16 views
7

Tengo una pregunta con respecto a los bloques de código en Perl. Dado el siguiente código:bloques de código perl

my @newArr = sort { $a <=> $b } @oldArr; 

usa un bloque de código como argumento.

pude volver a escribir como:

sub sortFunc { 
     return $a <=> $b; 
    } 
    my @newArr = sort sortFunc @oldArr; 

Estoy tratando de averiguar cómo funciona este mecanismo. Actualmente necesito implementar una clase de función de clasificación compleja que se vea desordenada en un bloque de código. Sin embargo, depende de algunas variables locales. Por ejemplo:

foreach my $val (@values){ 
     my @newArr = sort { $hash{$a}{$val}<=> $hash{$b}{$val} } @oldArr; 
     ... 
    } 

pero supongamos que la función de clasificación es más complejo, por lo que no lo puedo encajar perfectamente en el código de seguridad.

Si trato de usar una función (definida localmente en el alcance del bucle for), sigo recibiendo "Uso del valor no inicializado en el elemento hash".

Supongo que se debe a que el sub se analiza una vez y no se vuelve a crear para la iteración Evry del ciclo for. Estoy tratando de entender cómo implementar un bloque de código que sería reinterpretado en cada iteración, o tal vez cómo pasar los parámetros

+0

'mi @newArr = especie {$ a $ b} <=> @oldArr;' ¿qué significa el @oldArr acuerdo con esto? –

+0

"pero supongamos que la función de clasificación es más compleja" -> tal vez debería publicar su código para esta función de clasificación. – TLP

Respuesta

9

Usted no se presentó el código problemático por alguna razón, pero yo creo que es algo así como

for my $val (@values) { 
    sub sort_func { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    } 

    my @newArr = sort sort_func @oldArr; 
} 
Estoy tratando de averiguar cómo funciona este mecanismo. [...] Supongo que es porque el sub se analiza una vez y no se recrea para la iteración Evry del ciclo for.

No del todo. Los siguientes sólo se analiza y se compila la secundaria una vez, y sin embargo funciona:

for my $val (@values) { 
    my $cmp_func = sub { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    }; 

    my @newArr = sort $cmp_func @oldArr; 
} 

Lo que importa es cuando lo $val es capturado. $val se captura cuando se evalúa sub { ... }.Teniendo en cuenta que

sub foo { ... } 

es el mismo que el siguiente en este sentido,

BEGIN { *foo = sub { ... }; } 

En mi código, que captura la $val del bucle foreach. En el suyo, captura en tiempo de compilación, por lo que captura el $val que existía en el momento de la compilación. Y esa no es la variable que quieres.

Ahora que ya sabe cómo hacer que funcione, puede mover el código complejo fuera del camino (loop out) como lo desee.

sub make_cmp_func { 
    my ($hash, $val) = @_; 
    return sub { 
     return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
    }; 
} 

for my $val (@values) { 
    my $cmp_func = make_cmp_func(\%hash, $val); 
    my @newArr = sort $cmp_func @oldArr; 
} 

Alternativamente, puede pasar los valores necesarios para la función de comparación en lugar de la captura de ellos.

sub cmp_func { 
    my ($hash, $val, $a, $b) = @_; 
    return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
} 

for my $val (@values) { 
    my @newArr = sort { cmp_func(\%hash, $val, $a, $b) } @oldArr; 
} 
+0

Disculpe la longitud, pero preguntaste cómo funciona, no solo para encontrar una solución. – ikegami

+0

Gracias, Estaba considerando usar una referencia de código en lugar de una función, pero no estaba seguro de si eso podría hacer algo diferente. – Smartelf

+0

En su último ejemplo, ¿es necesario pasar y volver a declarar '$ a' y' $ b'? – TLP

8

Desea utilizar una función que tenga argumentos además de $a y $b.

sub my_sort_func { 
    my ($val, $a, $b) = @_; 
    return $hash{$a}{$val} <=> $hash{$b}{$val}; 
} 

foreach my $val (@values) { 
    my @newArr = sort { my_sort_func($val,$a,$b) } @oldArr; 
    ... 
} 

mecanismo de Perl para el uso de un bloque de código con sort es algo especial, y no replicado fácilmente en puro Perl.

5

Ampliando la respuesta de la mafia, esta es una de las variedades "inteligentes pero no necesariamente inteligentes". Si objeta el parámetro adicional, puede usar currying en su lugar.

sub make_sorter { 
    my ($hashref, $val) = @_; 
    return sub { 
      $hashref->{$a}{$val} <=> $hashref->{$b}{$val} 
    }; 
} 

for my $val (@values) { 
    my $sorter = make_sorter(\%hash, $val); 
    my @newArr = sort $sorter @oldArr; 
} 

Esto no es más eficiente, más legible, o más valioso en realidad cualquier manera, pero podría ser interesante saber acerca de la técnica de un lugar donde es realmente útil.

+0

Empecé en este camino también y cambié mi respuesta cuando lo obtuve para 'ordenar {make_sorter ($ hash, $ val) -> ($ a, $ b)} @ oldArr' ... – mob

Cuestiones relacionadas