2010-11-03 12 views
10

Siempre estuve seguro de que si paso una subrutina Perl a un escalar simple, nunca puede cambiar su valor fuera de la subrutina. Es decir:¿Cómo funciona @_ en las subrutinas Perl?

my $x = 100; 
foo($x); 
# without knowing anything about foo(), I'm sure $x still == 100 

Así que si quiero cambiar foo()x, que debe pasar una referencia a x.

Luego descubrí este no es el caso:

sub foo { 
$_[0] = 'CHANGED!'; 
} 
my $x = 100; 
foo($x); 
print $x, "\n"; # prints 'CHANGED!' 

Y lo mismo pasa con elementos de la matriz:

my @arr = (1,2,3); 
print $arr[0], "\n"; # prints '1' 
foo($arr[0]); 
print $arr[0], "\n"; # prints 'CHANGED!' 

Eso me sorprendió un poco. ¿Como funciona esto? ¿No es la subrutina solo obtiene el valor del argumento? ¿Cómo sabe su dirección?

+5

Perl no es C. No espere que se comporte como C, o cualquier lenguaje derivado de C como C++ o Java. –

+9

[perldoc perlsub] (http://perldoc.perl.org/perlsub.html) – Ether

+1

Su pregunta es sobre '@ _', no' $ _'. Y '@ _' en un' sub' a menudo contiene alias en lugar de copias de valores. Por lo tanto, si no desea este comportamiento en su 'sub', asegúrese de copiar la entrada de' @ _' a 'mis' variables al inicio. – aschepler

Respuesta

19

En Perl, los argumentos de la subrutina almacenados en @_ son siempre alias de los valores en el sitio de la llamada. Este alias solo persiste en @_, si copia valores, eso es lo que obtiene, valores.

lo que en este substitución:

sub example { 
    # @_ is an alias to the arguments 
    my ($x, $y, @rest) = @_; # $x $y and @rest contain copies of the values 
    my $args = \@_; # $args contains a reference to @_ which maintains aliases 
} 

Tenga en cuenta que este aliasing ocurre después de expansión de la lista, por lo que si usted pasó una matriz para example, la matriz se expande en el contexto de lista, y @_ se ajusta a los alias de cada elemento de la matriz (pero la matriz en sí no está disponible para example). Si quisieras lo último, pasarías una referencia a la matriz.

Aliasing de argumentos de subrutina es una característica muy útil, pero se debe utilizar con cuidado. Para evitar la modificación involuntaria de variables externas, en Perl 6 debe especificar que desea argumentos con alias editables con is rw.

Uno de los trucos menos conocidos pero útil es usar esta característica aliasing para crear refs matriz de alias

my ($x, $y) = (1, 2); 

my $alias = sub {\@_}->($x, $y); 

$$alias[1]++; # $y is now 3 

o rodajas alias:

my $slice = sub {\@_}->(@somearray[3 .. 10]); 

También resulta que el uso de sub {\@_}->(LIST) a crear una matriz a partir de una lista es realmente más rápido que
[ LIST ] ya que Perl no necesita copiar todos los valores. Por supuesto, la desventaja (o alcista dependiendo de su perspectiva) es que los valores siguen siendo alias, por lo que no puede cambiarlos sin cambiar los originales.

Como tchrist menciona en un comentario a otra respuesta, cuando se utiliza cualquiera de los constructos de solapamiento de Perl en @_, la $_ que proporcionan usted es también un alias a los argumentos a subrutinas originales. Tales como:

sub trim {s!^\s+!!, s!\s+$!! for @_} # in place trimming of white space 

Por último todos este comportamiento es encajable, por lo que cuando se utiliza @_ (o una porción de ella) en la lista de argumentos de otra subrutina, que también recibe los alias a los argumentos de la primera subrutina:

sub add_1 {$_[0] += 1} 

sub add_2 { 
    add_1(@_) for 1 .. 2; 
} 
2

Perl pasa argumentos por referencia, no por valor. Ver http://www.troubleshooters.com/codecorn/littperl/perlsub.htm

+1

+1, pero con la adición de que la referencia se rompe si el OP en su lugar escribe 'my $ notref = $ _ [0]; $ notref = 'UNCHANGED!'; 'Esto (y el beneficio adicional de los nombres legibles) es por qué la mayoría de las subrutinas comienzan por asignar todos los elementos en' @ _' a las variables nombradas. –

+4

El documento al que enlaza no se ha actualizado desde 2003, que es anterior a 5.8.8, a menudo se considera la versión "Permitida más recomendada" más antigua. Gran parte sigue siendo precisa, pero habrá algunas situaciones en las que la sintaxis se habrá modificado o mejorado. – Ether

+2

Pruebe la última persub en: http://perldoc.perl.org/perlsub.html o si desea una versión anterior: http://perldoc.perl.org/5.8.8/perlsub.html – daotoad

11

Todo esto está documentado en detalle en perldoc perlsub. Por ejemplo:

Cualquier argumento pasado en aparecer en la matriz @_. Por lo tanto, si llamó a una función con dos argumentos, estos se almacenarían en $ _ [0] y $ _ [1]. La matriz @_ es una matriz local, pero sus elementos son alias para los parámetros escalares reales. En particular, si se actualiza un elemento $ _ [0], se actualiza el argumento correspondiente (o se produce un error si no se puede actualizar). Si un argumento es un elemento de matriz o hash que no existía cuando se llamó a la función , ese elemento se crea solo cuando (y si) se modifica o se toma una referencia a él. (Algunas versiones anteriores de Perl creaban el elemento , sin importar si el elemento estaba asignado o no). Asignar a toda la matriz @_ elimina ese alias y no actualiza ningún argumento.

+2

Alguien debería mencionar que aliasing es transitivo, como con 'for (@_) {s/^ \ s + //; s/\ s + $ //} '. – tchrist

Cuestiones relacionadas