2010-11-03 6 views
10

Ero sempre sicuro che se passo una subroutine Perl a un semplice scalare, non può mai cambiare il suo valore al di fuori della subroutine. Cioè:Come funziona @_ nelle subroutine di Perl?

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

Quindi, se voglio foo() cambiare x, devo passare un riferimento a x.

Poi ho scoperto che non è questo il caso:

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

E lo stesso vale per gli elementi dell'array:

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

Questo genere di mi ha sorpreso. Come funziona? La subroutine non riceve il valore dell'argomento? Come fa a sapere il suo indirizzo?

+5

Perl non è C. Non aspettatevi che si comporti come C, o qualsiasi linguaggio derivato da C come C++ o Java. –

+9

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

+1

La tua domanda riguarda '@ _', non' $ _'. E '@ _' in un' sub' spesso contiene alias piuttosto che copie di valori. Quindi se non vuoi questo comportamento nel tuo 'sub', assicurati di copiare l'input da' @ _' alle variabili 'my' all'inizio. – aschepler

risposta

19

In Perl, gli argomenti di subroutine memorizzati in @_ sono sempre alias dei valori nel sito di chiamata. Questo aliasing persiste solo in @_, se si copiano valori, questo è ciò che si ottiene, valori.

quindi in questo sottogruppo:

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 
} 

noti che questa aliasing avviene dopo espansione lista, quindi se passato un array di example, la matrice si espande nel contesto lista, e @_ è impostato alias di ciascun elemento dell'array (ma l'array stesso non è disponibile per example). Se si voleva il secondo, si passerebbe un riferimento alla matrice.

L'alias degli argomenti di subroutine è una funzionalità molto utile, ma deve essere utilizzata con attenzione. Per impedire modifiche involontarie di variabili esterne, in Perl 6 è necessario specificare che si desidera argomenti con alias scrivibili con is rw.

Uno dei trucchi meno conosciuti ma utile è quello di utilizzare questa funzione aliasing per creare arbitri matrice di alias

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

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

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

o fette di alias:

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

risulta anche che l'uso sub {\@_}->(LIST) a creare un array da un elenco è in realtà più veloce di
[ LIST ] poiché Perl non ha bisogno di copiare ogni valore. Ovviamente lo svantaggio (o il rialzo a seconda della prospettiva) è che i valori rimangono alias, quindi non è possibile cambiarli senza cambiare gli originali.

Come tchrist menziona in un commento ad un'altra risposta, quando si utilizza uno dei costrutti di aliasing del Perl sul @_, il $_ che essi forniscono voi è anche un alias per gli argomenti originali subroutine. Come ad esempio:

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

Infine tutto questo comportamento è affiancabile, così quando si utilizza @_ (o una fetta di esso) nella lista degli argomenti di un'altra subroutine, si arriva anche alias per gli argomenti della prima subroutine:

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

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

Perl passa gli argomenti per riferimento, non per valore. Vedi http://www.troubleshooters.com/codecorn/littperl/perlsub.htm

+1

+1, ma con l'aggiunta che il riferimento si interrompe se l'OP scrive invece 'my $ notref = $ _ [0]; $ notref = 'UNCHANGED!'; 'Questo (e il vantaggio aggiunto dei nomi leggibili) è il motivo per cui la maggior parte delle subroutine iniziano assegnando tutti gli elementi in' @ _' alle variabili con nome. –

+4

Il documento a cui ci si collega non è stato aggiornato dal 2003, che precede il 5.8.8, spesso considerato la prima versione "consigliata" di Perl. Gran parte di essa è ancora accurata, ma ci saranno alcune situazioni in cui la sintassi è stata modificata o migliorata. – Ether

+2

Prova l'ultima versione su http://perldoc.perl.org/perlsub.html oppure vuoi una versione precedente: http://perldoc.perl.org/5.8.8/perlsub.html – daotoad

11

Questo è tutto documentato in dettaglio in perldoc perlsub. Ad esempio:

Eventuali argomenti passati visualizzati nell'array @_. Pertanto, se hai richiamato una funzione con due argomenti, questi verranno archiviati in $ _ [0] e $ _ [1]. L'array @_ è un array locale, ma gli elementi sono alias per i parametri scalari effettivi. In particolare, se un elemento $ _ [0] viene aggiornato, l'argomento corrispondente viene aggiornato (o si verifica un errore se non è aggiornabile). Se un argomento è un array o un elemento hash che non esisteva quando veniva chiamata la funzione , quell'elemento viene creato solo quando (e se) viene modificato o viene preso un riferimento ad esso. (Alcune versioni precedenti di Perl hanno creato l'elemento indipendentemente dal fatto che l'elemento sia stato o meno assegnato.) L'assegnazione all'intero array @_ rimuove l'aliasing e non aggiorna alcun argomento.

+2

Qualcuno dovrebbe menzionarlo l'aliasing è transitivo, come con 'for (@_) {s/^ \ s + //; s/\ s + $ //} '. – tchrist