2009-08-28 11 views
46

he oído que las personas no deberían usar para llamar & submarinos Perl, es decir:¿Cuándo debería usar & para llamar a una subrutina de Perl?

function($a,$b,...); 
# opposed to 
&function($a,$b,...); 

Sé que es una la lista de argumentos se convierte en opcional, pero ¿cuáles son algunos casos en los que es apropiado utilizar el & y los casos donde definitivamente no deberías estar usándolo?

Además, ¿cómo entra en juego el aumento de rendimiento aquí al omitir el &?

Respuesta

33

OMI, la única vez que haya ninguna razón para utilizar & es si usted está obteniendo o llamar a un coderef, como:

sub foo() { 
    print "hi\n"; 
} 

my $x = \&foo; 
&$x(); 

El tiempo principal que puede usarlo que absolutamente shouldn 't en la mayoría de las circunstancias es cuando se llama a un sub que tiene un prototipo que especifica cualquier comportamiento de llamada no predeterminado. Lo que quiero decir con esto es que algunos prototipos permiten la reinterpretación de la lista de argumentos, por ejemplo convirtiendo las especificaciones @array y %hash en referencias. Por lo tanto, el submarino esperará que se produzcan esas reinterpretaciones y, a menos que vaya a cualquier longitud que sea necesaria para imitarlas a mano, el submarinista obtendrá entradas muy diferentes de las que espera.

Creo que la mayoría de las personas intentan decirte que todavía estás escribiendo en el estilo Perl 4, y ahora tenemos una cosa mucho más limpia y más agradable llamada Perl 5.

En cuanto a rendimiento, hay varias maneras en que Perl optimiza las llamadas secundarias que & derrota, con uno de los principales siendo el entrante de las constantes.

También hay una circunstancia en la que el uso de & proporciona un beneficio de rendimiento: si reenvía una subllamada con foo(@_). El uso de &foo es infinitesimalmente más rápido que foo(@_). No lo recomendaría, a menos que haya encontrado definitivamente mediante el perfil que necesita esa micro-optimización.

+0

¿Podría ampliar la parte sobre "comportamiento de llamada no predeterminado"? –

+23

Excepto que '& $ x()' no es realmente la mejor manera de llamar a un código ref; probablemente debería desreferenciarlo, como cualquier otra ref: $ x ->(). –

+3

La única vez que uso '&' en una llamada es para 'AUTOLOAD '. El objetivo no es la optimización, sin embargo, es limpiar la pila de llamadas si alguien quiere hacer un seguimiento. –

16

El formulario de subrutina &() deshabilita la comprobación del prototipo. Esto puede o no ser lo que quieres.

http://www.perl.com/doc/manual/html/pod/perlsub.html#Prototypes

Prototipos le permiten especificar el número y tipo de sus argumentos a subrutinas, y que se verifiquen en tiempo de compilación. Esto puede proporcionar asistencia de diagnóstico útil.

Los prototipos no se aplican a las llamadas a métodos, o llamadas hechas en el estilo pasado de moda usando el prefijo &.

El & es necesario hacer referencia o eliminar la referencia de una subrutina o código de referencia

por ejemplo

sub foo { 
    # a subroutine 
} 

my $subref = \&foo; # take a reference to the subroutine 

&$subref(@args); # make a subroutine call using the reference. 

my $anon_func = sub { ... }; # anonymous code reference 
&$anon_func(); # called like this 

Los prototipos tampoco son aplicables a las referencias de subrutinas.

La forma de subrutina & también se utiliza en la llamada forma magic goto.

La expresión goto &subroutine reemplaza el contexto de la llamada actual con una llamada a la subrutina con nombre, utilizando el valor actual de @_.

Básicamente, puede cambiar por completo una llamada a una subrutina con una llamada al nombre. Esto se ve comúnmente en los bloques de AUTOLOAD, donde se puede realizar una llamada de subrutina diferida, quizás con alguna modificación a @_, pero se ve al programa por completo como si fuera una llamada al sub nombrado.

p. Ej.

sub AUTOLOAD { 
    ... 
    push @_, @extra_args; # add more arguments onto the parameter list 
    goto &subroutine ; # change call another subroutine, as if we were never here 
} 

}

Potencialmente esto podría ser útil para tail call elimination, supongo.

ver detailed explanation of this technique here

49

soy un abusador frecuente de &, pero sobre todo porque estoy haciendo algo raro interfaz. Si no necesita una de estas situaciones, no use el &. La mayoría de estos son solo para acceder a una definición de subrutina, no para llamar a una subrutina. Todo está en perlsub.

  1. Tomando como referencia una subrutina con nombre. Esta es probablemente la única situación común para la mayoría de Perlers:

    my $sub = \&foo; 
    
  2. Del mismo modo, la asignación a un typeglob, lo que le permite llamar a la subrutina con un nombre diferente:

    *bar = \&foo; 
    
  3. Comprobación de que una subrutina se define, como puede ser que en conjuntos de pruebas:

    if(defined &foo) { ... } 
    
  4. Extracción de una definición de una subrutina, que no debe ser común:

    undef &foo; 
    
  5. Proporcionar una subrutina despachador cuyo único trabajo es elegir el derecho de llamar a la subrutina. Esta es la única situación que utilizo & a llamada una subrutina, y cuando espero que llamar al despachador muchas, muchas veces y tenga que apretar un poco el rendimiento de la operación:

    sub figure_it_out_for_me { 
        # all of these re-use the current @_ 
         if(...some condition...) { &foo  } 
        elsif(...some other... ) { &bar  } 
        else       { &default } 
        } 
    
  6. Para saltar en otra subrutina utilizando la pila argumento actual (y la sustitución de la subrutina actual en la pila de llamadas), una operación unrare en el envío, especialmente en AUTOLOAD:

    goto ⊂ 
    
  7. llamar a una subrutina que usted ha nombrado después de un Perl construido -en. El & siempre le da el definido por el usuario. Eso es why we teach it in Learning Perl. En realidad, no desea hacerlo normalmente, pero es una de las características de &.

hay algunos lugares donde se puede utilizar, pero hay mejores maneras:

  1. para llamar a una subrutina con el mismo nombre que un Perl incorporado. Simplemente no tiene subrutinas con el mismo nombre que un Perl incorporado. Consulte perlfunc para ver la lista de nombres incorporados que no debe usar.

  2. Para deshabilitar prototipos. Si no sabe lo que eso significa o por qué lo quiere, no use el &. Es posible que algún código de magia negra lo necesite, pero en esos casos probablemente sepa lo que está haciendo.

  3. Para eliminar la referencia y ejecutar una referencia de subrutina. Simplemente use la notación ->.

+0

¿De verdad quieres hacer 'defined & foo', parece que' __PACKAGE __-> can ('foo') 'es más" moderno "? Me interesan las opiniones –

+2

'can' no dice si se define una subrutina. Le dice si una subrutina llamada 'can' quiere que piense que está definida, incluso si no lo es. –

+6

Además, '-> can' sigue a la herencia. No le dice si la subrutina realmente está en el paquete actual. –

1

He leído los argumentos en contra de usar '&', pero casi siempre lo uso. Me ahorra mucho tiempo para no hacerlo. Paso una fracción muy grande de mi tiempo de codificación de Perl buscando qué partes del código llaman a una función en particular. Con un & líder, puedo buscarlos y encontrarlos al instante. Sin un & líder, obtengo la definición de la función, los comentarios y las declaraciones de depuración, generalmente triplicando la cantidad de código que tengo que inspeccionar para encontrar lo que estoy buscando.

Lo principal es que no se usa '&' y es que le permite usar prototipos de funciones. Pero los prototipos de la función Perl pueden crear errores tan a menudo como lo previenen, ya que tomarán su lista de argumentos y la reinterpretarán en formas que usted no esperaría, de modo que su llamada de función ya no pasa los argumentos que literalmente dice que lo hace.

+2

de acuerdo. en una base de código que lo usa de manera consistente, y puede ser útil. los únicos inconvenientes son no permitir prototipos (si se puede llamar así a un inconveniente) o la posibilidad de decir accidentalmente '& foo' en lugar de' & foo() 'o accidentalmente usarlo en una constante y suprimir la constante en línea. Estas desventajas no son suficientes para desalentar * si elige *. – ysth

Cuestiones relacionadas