2009-04-29 23 views
32

Ho una funzione che accetta una variabile e un array associativo, ma non riesco a farle passare a destra. Penso che questo abbia qualcosa a che fare con le dichiarazioni di funzione, tuttavia non riesco a capire come funzionano in Perl. C'è un buon riferimento per questo e come posso realizzare ciò di cui ho bisogno?Come posso passare un hash a una funzione in Perl?

Devo aggiungere che deve essere passato per riferimento.

sub PrintAA 
{ 
    my $test = shift; 
    my %aa = shift; 
    print $test . "\n"; 
    foreach (keys %aa) 
    { 
     print $_ . " : " . $aa{$_} . "\n"; 
     $aa{$_} = $aa{$_} . "+"; 
    } 
} 
+0

Puoi mostrarci il codice? –

+0

Puoi mostrarci il codice chiamante? –

risposta

60

Passare il riferimento anziché l'hash stesso. Come in

PrintAA("abc", \%fooHash); 

sub PrintAA 
{ 
    my $test = shift; 
    my $aaRef = shift; 

    print $test, "\n"; 
    foreach (keys %{$aaRef}) 
    { 
    print $_, " : ", $aaRef->{$_}, "\n"; 
    } 
} 

Vedi perlfaq7 anche: How can I pass/return a {Function, FileHandle, Array, Hash, Method, Regex}?

+0

Anche se non come pensavo fosse necessario, questo è il metodo più semplice. – rlbond

4

Sembra che si dovrebbe passare un riferimento ad un hash.

sub PrintAA 
{ 
    my $test = shift; 
    my $aa = shift; 
    if (ref($aa) != "HASH") { die "bad arg!" } 
    .... 
} 

PrintAA($foo, \%bar); 

Il motivo non si può fare un

my %aa = shift; 

è perché Perl appiattisce tutti gli argomenti ad una subroutine in un unico elenco, @_. Ogni elemento viene copiato, quindi passare per riferimento evita anche quelle copie.

12

alternativa:

sub PrintAA 
{ 
    my $test  = shift; 
    my %aa   = @_; 
     print $test . "\n"; 
     foreach (keys %aa) 
     { 
       print $_ . " : " . $aa{$_} . "\n"; 
       $aa{$_} = $aa{$_} . "+"; 
     } 
} 

La cosa si sta fondamentalmente manca è che un array associativo non è un singolo argomento (anche se un riferimento array associativo è, come nella risposta di Paolo Tomblin).

3

Come al solito ci sono diversi modi. Ecco cosa Perl Best Practices, che la maggior parte venerato di puntatori stile, ha da dire in proposito il passaggio di parametri a funzioni:

Utilizzare un hash di argomenti con nome per qualsiasi subroutine che ha più di tre parametri

ma dal momento che si hanno solo due, si potrebbe ottenere via;) con il passare direttamente in questo modo:

my $scalar = 5; 
my %hash = (a => 1, b => 2, c => 3); 

func($scalar, %hash) 

E funzione è definita in questo modo:

sub func { 
    my $scalar_var = shift; 
    my %hash_var = @_; 

    ... Do something ... 
} 

Potrebbe essere più utile se fosse possibile mostrare del codice.

15

Questo codice funziona:

#!/bin/perl -w 

use strict; 

sub PrintAA 
{ 
    my($test, %aa) = @_; 
    print $test . "\n"; 
    foreach (keys %aa) 
    { 
     print $_ . " : " . $aa{$_} . "\n"; 
    } 
} 

my(%hash) = ('aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA); 

PrintAA("test", %hash); 

Il punto chiave è l'uso del contesto array nella mia() 'istruzione' nella funzione.


Che cosa significa il contesto di business serie effettivamente fare?

In modo succinto, funziona correttamente.

Significa che il primo valore nell'array @_ di argomenti è assegnato a $test e che gli elementi rimanenti sono assegnati all'hash %aa. Dato il modo in cui l'ho chiamato, c'è un numero dispari di articoli nel @_, quindi una volta assegnato il primo elemento a $test, c'è un numero pari di elementi disponibili da assegnare a %aa, con il primo elemento di ciascuna coppia che è il key ('aaa', 'bbb', 'ccc' nel mio esempio), e il secondo è il valore corrispondente.

Sarebbe possibile sostituire %aa con @aa, nel qual caso l'array avrà 6 elementi. Sarebbe anche possibile sostituire %aa con $aa e, in tal caso, la variabile $aa conterrà il valore 'aaa' e i valori rimanenti in @_ verranno ignorati dall'assegnazione.

Se si omettono le parentesi attorno all'elenco delle variabili, Perl rifiuta di compilare il codice. Una delle risposte alternative hanno mostrato la notazione:

my $test = shift; 
my(%aa) = @_; 

Questo è più o meno equivalente a quello che ho scritto; la differenza è che dopo le due istruzioni my, @_ contiene solo 6 elementi in questa variante, mentre nella versione singola my contiene ancora 7 elementi.

Ci sono altre domande nello SO sul contesto dell'array.


In realtà, non è stato chiesto circa la my($test, %aa) = @_; stavo chiedendo my(%hash) = ('aaa' => 1, 'bbb' => 'balls', 'ccc' => \&PrintAA); contro my %hash = { 'aaa' => 1, ... };

La differenza è che il {...} notazione genera un ref hash e il (...) genera una lista, che mappa ad un hash (al contrario di hash ref). Allo stesso modo, [...] genera un array ref e non un array.

Effettivamente, modificare il codice "principale" in modo che legga: my (% hash) = {...}; e si ottiene un run-time (ma non tempo di compilazione) Errore - numeri di linea trattare con cautela, in quanto ho aggiunto codifiche alternative al mio file:

Reference found where even-sized list expected at xx.pl line 18. 
... 
Use of uninitialized value in concatenation (.) or string at xx.pl line 13. 
+2

TMTOWTDI, ma preferisco questo approccio. – spoulson

+0

Cosa fa in realtà il contesto aziendale dell'array? –

+0

In realtà, non stavo chiedendo il mio my ($ test,% aa) = @_; Mi stavo chiedendo di mio (% hash) = ('aaa' => 1, 'bbb' => 'palle', 'ccc' => \ & PrintAA); versus my% hash = {'aaa' => 1, ...}; –

3

Tutti i metodi di cui sopra funzionano, ma questo è sempre stato il modo in cui Ho preferito fare cose del genere:

sub PrintAA ($\%) 
{ 
    my $test  = shift; 
    my %aa   = ${shift()}; 
    print "$test\n"; 
    foreach (keys %aa) 
    { 
     print "$_ : $aa{$_}\n"; 
     $aa{$_} = "$aa{$_}+"; 
    } 
} 

Nota: ho anche modificato un po 'il codice. Le stringhe con quotatura doppia di Perl interpreteranno "$test" come il valore di $test anziché la stringa effettiva '$test', quindi non è necessario disporre di molti . s.

Inoltre, ho sbagliato su come funzionano i prototipi.Per passare un hash, utilizzare questo:

PrintAA("test", %hash); 

Per stampare un riferimento ad hash, utilizzare questo:

PrintAA("test", %$ref_to_hash); 

Naturalmente, ora non è possibile modificare l'hash a cui fa riferimento $ref_to_hash perché si sta inviando una copia, ma è possibile modificare un grezzo %hash perché lo si passa come riferimento.

+0

Downvoted perché 1) questo codice non funziona e 2) I prototipi Perl non fanno ciò che la maggior parte delle persone (apparentemente compreso voi) si aspetta che facciano. Sono utili per emulare il comportamento delle funzioni incorporate, ma non sono molto utili in altro modo. La parte "\%" del prototipo dice che il secondo argomento deve essere un hash e passarlo per riferimento. Deve essere un * reale * hash. Non può essere un hashref o un elenco di coppie chiave => valore. –

+0

Grazie per le correzioni. Non ha senso che \% non possa essere un riferimento, ma è vero. Non penso che passerà un elenco di coppie chiave => valore, in quanto la sua funzione sembra modificare l'hash da lui inserito come parametro, ma gli altri punti sono validi. –

0

Utilizzare il sub seguente per ottenere hash o rifhash - qualunque sia superato :)

sub get_args { ref($_[0]) ? shift() : (@_ % 2) ? {} : {@_}; } 
sub PrintAA 
{ 
    my $test = shift; 
    my $aa = get_args(@_);; 
    #then 
    $aa->{somearg} #do something 
    $aa->{anotherearg} #do something 

} 

Chiama la funzione in questo modo:

printAA($firstarg,somearg=>1, anotherarg=>2) 

O come questo (non importa):

printAA($firstarg,{somearg=>1, anotherarg=>2}) 

O anche questo (non importa):

my(%hash) = ('aaa' => 1, 'bbb' => 'balls', 'ccc' => \PrintAA); 

PrintAA("test", %hash); 

Cheers!

+0

L'utilizzo di prototipi può consentire di fare la stessa cosa, oltre a dare un controllo in fase di compilazione degli argomenti della subroutine. Inoltre, i tuoi get_args saranno ingannati da una serie di riferimenti. –

+0

È per hash (ref) :) - So che non funziona con gli array –

+0

@Chris - i prototipi non hanno alcun effetto quando una subroutine viene invocata come metodo, sono orribili e confusionari e rompono le cose. Come qualcun altro ha postato in precedenza, non usarli a meno che non sia necessario creare un sottoprocesso come un built-in. – converter42

1

Gli argomenti delle funzioni vengono appiattiti in un singolo array (@_). Di solito è più semplice passare gli hash per funzionare come riferimento.

Per creare un hash:

my %myhash = (key1 => "val1", key2 => "val2"); 

Per creare un riferimento a tale hash:

my $href = \%myhash 

per accedere a tale hash per riferimento;

%$href 

Quindi nel tuo sub:

my $myhref = shift; 

keys %$myhref; 
1

Tutte le altre risposte qui finora sembra piuttosto complicato per me. Quando scrivo la funzione Perl, di solito "espandi" tutti gli argomenti passati nella prima riga della funzione.

sub someFunction { 
    my ($arg1, $arg2, $arg3) = @_; 

Questo è simile ad altri linguaggi, in cui si dichiara funzioni

... someFunction (arg1, arg2, arg3) 

E se lo si fa in questo modo e passare l'hash come ultimo argomento, andrà tutto bene senza trucchi o magia speciale. Ad esempio:

sub testFunc { 
    my ($string, %hash) = @_; 
    print "$string $hash{'abc'} $hash{'efg'} $string\n"; 
} 

my %testHash = (
    'abc' => "Hello", 
    'efg' => "World" 
); 
testFunc('!!!', %testHash); 

L'uscita è come previsto:

!!! Hello World !!! 

Questo funziona becaus in argomenti Perl vengono sempre passati come una matrice di valori scalari e se si passa un hash, il suo valore chiave/coppie sono aggiunto a quell'array. Nell'esempio di cui sopra, gli argomenti passati alla funzione come matrice (@_) sono infatti:

'!!!', 'abc', 'Hello', 'efg', 'World' 

e '!!!'è semplice assegnato a %string, mentre %hash "inghiotte" tutti gli altri argomenti, interpretando sempre uno come chiave e il successivo come valore (fino a quando tutti gli elementi sono esauriti).

Non è possibile passare più hash in questo modo e l'hash non può essere il primo argomento, altrimenti si ingerisce tutto e non tutti gli altri argomenti vengono assegnati.

Ovviamente esattamente lo stesso funziona per array come ultimo argomento. L'unica differenza qui è che gli array non distinguono tra chiavi e valori, poiché tutti gli argomenti rimasti sono valori e vengono semplicemente inviati all'array.

Problemi correlati