2011-06-30 8 views
9

Siempre he estado algo confundido sobre el propósito y el uso de subs anónimos en perl. Entiendo el concepto, pero busco ejemplos y explicaciones sobre el valor de esta práctica.Explorando los usos de subs anónimos

Para que quede claro:

sub foo { ... } # <--- named sub 
sub { ... }  # <--- anonymous sub 

Por ejemplo:

$ perl -e 'print sub { 1 }' 
CODE(0xa4ab6c) 

me dice que sub devuelve un valor escalar. Entonces, puedo hacer:

$ perl -e '$a = sub { 1 }; print $a' 

Para la misma salida que el anterior. Esto, por supuesto, es válido para todos los valores escalares, por lo que puede cargar matrices o hashes con subs anónimos.

La pregunta es, ¿cómo utilizo estos subs? ¿Por qué querría usarlos?

Y para una estrella dorada, ¿hay algún problema que solo pueda resolverse con un submarino anónimo?

+2

"Y para un oro estrella, ¿hay algún problema que solo pueda resolverse con un sub anónimo? " Nop. ¿Dónde está mi estrella de oro? : P –

+7

Más ejemplos de los que puede agitar un palo en: http://hop.perl.plover.com/ – friedo

+1

@Chris Está en el correo. – TLP

Respuesta

9

Las subrutinas anónimas se pueden utilizar para todo tipo de cosas.

  1. devoluciones de llamada para sistemas de manejo de eventos:

    my $obj = Some::Obj->new; 
    
    $obj->on_event(sub {...}); 
    
  2. Iteradores:

    sub stream {my $args = \@_; sub {shift @$args}} 
    
    my $s = stream 1, 2, 3; 
    
    say $s->(); # 1 
    say $s->(); # 2 
    
  3. funciones de orden superior:

    sub apply (&@) { 
        my $code = shift; 
        $code->() for my @ret = @_; 
        @ret 
    } 
    
    my @clean = apply {s/\W+/_/g} 'some string', 'another string.'; 
    
    say $clean[0]; # 'some_string' 
    
  4. Creación de alias arrays ED:

    my $alias = sub {\@_}->(my $x, my $y); 
    
    $alias[0]++; 
    $alias[1] = 5; 
    
    say "$x $y"; # '1 5'' 
    
  5. programación dinámica con cierres (tales como crear un montón de subrutinas que sólo se diferencian por una pequeña cantidad):

    for my $name (qw(list of names)) { 
        no strict 'refs'; 
        *$name = sub {... something_with($name) ...}; 
    } 
    

No hay situación en la que una subrutina anónima puede hacer cualquier cosa que una subrutina nombrada no pueda.El constructor my $ref = sub {...} es equivalente a la siguiente:

sub throw_away_name {...} 

my $ref = \&throw_away_name; 

sin tener que preocuparse de decidir sobre un 'throw_away_name' único para cada sub.

La equivalencia también va por el otro, con sub name {...} es equivalente a:

BEGIN {*name = sub {...}} 

Así, aparte del nombre, la referencia de código creado por cualquiera de los métodos es el mismo.

Para llamar a una referencia de subrutinas, puede utilizar cualquiera de los siguientes:

$code->();   # calls with no args 
$code->(1, 2, 3); # calls with args (1, 2, 3) 
&$code();   # calls with no args 
&$code;   # calls with whatever @_ currently is 

Incluso puede utilizar códigos de referencia como métodos de escalares benditos o sin bendecir:

my $list = sub {@{ $_[0] }}; 

say for [1 .. 10]->$list # which prints 1 .. 10 
+1

Una respuesta exhaustiva y clara, ¡gracias! – TLP

+0

Una advertencia que encontré hoy parece indicar que hay algo que una subrutina anónima puede manejar que una llamada con nombre no siempre hará correctamente: 'La variable"% s "no permanecerá compartida en ...' – Izkata

3

Se usan generalmente cuando quiere pasar un sub a otro bit de código. A menudo, este es un caso de "Cuando ocurre X (en código de terceros), haga Y".

Por ejemplo. Al definir un atributo en Moose, puede especificar el valor predeterminado de ese atributo utilizando un sub. Dada una clase que tiene, como parte de su definición:

has 'size' => (
     is => 'ro', 
     default => 
      sub { ('small', 'medium', 'large')[ int(rand 3) ] }, 
     predicate => 'has_size', 
); 

Siempre que se crea una instancia de esa clase sin un tamaño explícito que se pasa, el sub será llamado y el valor de retorno será el tamaño de ese objeto .

Si cambiamos a otro idioma para dar un ejemplo diferente, encontrará un concepto similar en JavaScript.

var b = document.getElementById('my_button'). 
b.addEventListener('click', function (e) { alert('clicked!'); }); 
4

Bueno, escribí un analizador de SAX para Perl que está basado en eventos. Puede pasar subs anónimos a los eventos de inicio/final en un elemento.

my $str = "<xml><row><data></data></row></xml>": 

my $parser = SAXParser->new(); 

$parser->when('row')->begin(sub { 
    my ($element) = @_; 
    push(@rows, $row); 
}); 

$parser->when('row')->end(sub { 
    ## do something like serialize it or whatever 
}); 

$parser->parse($str); 
8

Puede usarlo para crear iteradores.

use strict; 
use warnings; 

use 5.012; 

sub fib_it { 
    my ($m, $n) = (0, 0); 

    return sub { 
    my $val = ($m + $n); 
    $val = 1 unless $val; 
    ($m, $n) = ($n, $val); 
    return $val; 
    } 
} 

my $fibber = fib_it; 
say $fibber->() for (1..3); ### 1 1 2 

my $fibber2 = fib_it; 
say $fibber2->() for (1..5); ### 1 1 2 3 5 
say $fibber->() for (1..3); #### 3 5 8 
+0

+1 Una buena, pero algo difícil de comprender explicación y ejemplo. :) Gracias. – TLP

7

Aquí es algo similar que podría haber visto antes:

@new_list = map { $_ + 1 } @old_list; 

Y también:

@sorted = sort { $a <=> $b } @unsorted; 

Ninguno de los que son subs anónimos, pero su comportamiento puede ser imitado en sus funciones con subs anónimos No necesitan la palabra clave sub porque las funciones son (esencialmente) prototipadas para que su primer argumento sea una subrutina, y Perl lo reconoce como un caso especial donde sub se puede dejar. (Las funciones también establecen las variables necesarias en valores significativos antes de llamar a las subrutinas que proporcionó para simplificar la aprobación de argumentos, pero eso no está relacionado.)

Puede escribir su propia función map -como:

sub mapgrep (&@) { # make changes and also filter based on defined-ness 
    my ($func, @list) = @_; 
    my @new; 
    for my $i (@list) { 
    my $j = $func->($i); 
    push @new, $j if defined $j; 
    } 
} 

La magia para hacer que funcione con $ _ es un poco mucho para escribir aquí - la versión anterior sólo funciona para los submarinos que toman argumentos.

+0

si el primer ejemplo sería como un sub anónimo, agregando un 'return' no terminaría la función, sino solo el' map'. Pruebe sub foo { my @old_list = (1 .. 3); my @new_list = map {return $ _ + 1} @old_list; print "end of foo \ n"; } foo(); print "end \ n"; – hexcoder

+0

"Ambos son subs anónimos". No, no lo son. Ver el comentario de hexcoder. Son bloques de código anónimos que son 'evalu'ed, no llamados. –

+0

Pensé en ese contexto (mapa/ordenar) que se llamaban bloques. Intenté usar la palabra clave sub en ese contexto, y fue un error de sintaxis. – TLP

8

Las subrutinas anónimas se pueden utilizar para crear cierres.

El cierre es una noción fuera del mundo de Lisp que dice que si defines una función anónima en un contexto léxico particular, pretende ejecutarse en ese contexto incluso cuando se invoca fuera del contexto.

+0

¿Podría darnos un ejemplo? – TLP

+1

una nit menor, las subrutinas con nombre normal también pueden ser cierres: '{my $ x; subcuento {$ x ++}} '. –

2

En su ejemplo, que en realidad no han llamada subrutina creada. La llamada se realiza con & $ a o $ a ->() sintaxis. Lo que ha hecho es que almacenó una referencia en subrutina en $ a, luego la modificó y la imprimió. Compare:

my $a = sub {1}; 
my $b = sub {1}; 
print join("\n", $a, $a->(), $b, $b->()); 
+0

+1 explicación muy concisa, gracias. – TLP

2

Estos son los subs para el programador perezoso. Puede usarlos para funciones locales desechables y puede guardar algo de tipeo. En lugar de

sub x { ... } 
my $function_ptr = \&x; 

ahora se puede utilizar

my $function_ptr = sub { ... }; 

Las funciones anónimas también son privadas, y sólo se puede acceder a través de la $function_ptr, por lo que no tiene una entrada en la tabla de símbolos.

Cuestiones relacionadas