2015-09-04 12 views
5

Ho un subroutine con un prototipo del genere:SP con hash e facoltativo argomento scalare

sub printFoo(%) { 
    my (%hashFoo)[email protected]_; 
    # do stuff with the hash 
} 

Ma vorrei facoltativamente passare in un secondo argomento scalare in questo modo:

sub printFoo(%;$) { 
    my (%hashFoo,$optionalArg)[email protected]_; 
    # do stuff with the hash and the optional arg 
} 

I capisci che in modalità di avviso questo è un no-no, ma non sono sicuro del perché.

Suppongo che potrei usare un flag di variabile globale, ma qualche consiglio su come realizzare elegantemente questo tipo di firma di funzione?

+1

È possibile passare un riferimento all'hash anziché all'hash stesso –

+5

I prototipi non servono per la creazione di firme di funzioni, ma per il fatto che le funzioni si comportano come funzioni integrate. Il consiglio generale quando si tratta di prototipi, non è quello di usarli. La funzione recupera tutti i @_ in% hashFoo. Puoi controllare la lunghezza di @_ e inserire l'argomento facoltativo prima di decomprimerlo in% hashFoo. – xxfelixxx

+1

La soluzione più pulita sarebbe quella di avere l'argomento opzionale solo da includere con il resto dei parametri (foo => bar, baz => 123, speciale => 3), e trattare il parametro speciale in modo speciale (con un valore predefinito se è non passato in) – xxfelixxx

risposta

4

Non so se questo conta come elegante, ma ...

sub printFoo { 
    my $optionalArg; 
    if (@_ % 2 != 0) { 
     $optionalArg = pop @_; 
    } 
    my %hashFoo = @_; 
    ... 
} 

un prototipo con un riferimento ad hash sarebbe anche lavorare. Avresti comunque invocato la funzione con un hash, ma devi ricordare che il primo hash arg verrà ricevuto dal tuo sub come riferimento di hash.

sub printFoo (\%;$) { # call as printFoo %foo or printFoo %foo, 42 
    my ($hashFooRef, $optionalArg) = @_; 
    my %hashFoo = %$hashFooRef; 
    ... 
} 
1

Sono d'accordo con Hakon Haegland sull'utilizzo di un ref hash. Per ottenere più di un argomento che è possibile selezionare, è necessario passare più scalari piuttosto che essenzialmente un elenco seguito da qualcos'altro.

Penso che questo non sia correlato alla questione se si debba o meno usare i prototipi. Il sistema di allerta ti ha favorito segnalandolo, ma sono sicuro al 99.44% che non funzionerà anche se scarti il ​​prototipo. Ancora non si otterrà un valore nel parametro facoltativo.

2

Elegantemente che fare con un parametro opzionale:

sub do_something { 
    my (%params) = @_; 
    my $debug = delete $params{debug} || 0; 
    # do something with %params... 
} 
+0

Questo è vicino a come lo faccio personalmente in quasi tutti i miei moduli. Accetto un singolo href (invece di un hash) per tutti i miei parametri. A volte accetterò un callback aggiuntivo di cref come argomento secondario, ma preferisco accettare tutto in una struttura. – stevieb

2

Se si utilizza un ref hash come altri hanno suggerito come il primo arg, poi controllando i args dopo che sono state accettate è banale:

use strict; 
use warnings; 

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

printFoo(\%hash, $scalar); 

sub printFoo { 
    my ($href, $opt) = @_; 

    if(ref $href ne 'HASH' || $opt && ref \$opt ne 'SCALAR'){ 
     die "Usage: printFoo(hashref, optional_scalar)\n"; 
    } 

    ... 
} 
Problemi correlati