2009-11-13 13 views

risposta

44

ref():

Perl fornisce la funzione ref() in modo da poter controllare il tipo di riferimento prima dereferencing un riferimento ...

Utilizzando la funzione ref() è possibile proteggere il codice del programma che dereferenzia variabili da produrre errori quando viene utilizzato il tipo di riferimento sbagliato ...

+1

Ho pensato che ref() ti direbbe solo che tipo di riferimento è e non restituirà nulla se non lo è. –

+3

thx - beh, volevo solo dire thx - ma non è consentito – pm100

+7

@Chris: Giusto, quindi se la variabile non è un riferimento, è possibile dedurre che si tratta di un semplice scalare dal fatto che non restituisce nulla. Altrimenti, saprai che tipo di riferimento è. –

40

$x è sempre uno scalare. Il suggerimento è il sigillo $: qualsiasi variabile (o dereferenziazione di qualche altro tipo) a partire da $ è uno scalare. (Vedere perldoc perldata per ulteriori informazioni sui tipi di dati.)

Un riferimento è solo un particolare tipo di scalare. La funzione integrata ref indica quale tipo di riferimento è. D'altra parte, se hai un riferimento benedetto, ref ti dirà solo il nome del pacchetto in cui è stato inserito il riferimento, non il vero tipo di core dei dati (i riferimenti benedetti possono essere hashrefs, arrayrefs o altre cose). È possibile utilizzare Scalar::Util s' reftype vi dirà che tipo di riferimento è:

use Scalar::Util qw(reftype); 

my $x = bless {}, 'My::Foo'; 
my $y = { }; 

print "type of x: " . ref($x) . "\n"; 
print "type of y: " . ref($y) . "\n"; 
print "base type of x: " . reftype($x) . "\n"; 
print "base type of y: " . reftype($y) . "\n"; 

... produce l'uscita:

type of x: My::Foo 
type of y: HASH 
base type of x: HASH 
base type of y: HASH 

Per ulteriori informazioni su altri tipi di riferimenti (per esempio rifcodice , arrayref ecc.), vedere questa domanda: How can I get Perl's ref() function to return REF, IO, and LVALUE? e perldoc perlref.

Nota: Si dovrebbe non uso ref per implementare rami di codice con un oggetto benedetto (ad es $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) - se avete bisogno di prendere decisioni in base al tipo di una variabile, utilizzare isa (cioè if ($a->isa("My::Foo") { ... o if ($a->can("foo") { ...) . Vedi anche polymorphism.

+3

Nota che reftype, per definizione, viola l'incapsulamento, quindi dovrebbe essere evitato a meno che tu non abbia una buona ragione. – ysth

+2

Se si usa reftype, si tenga presente che restituisce undef per un non riferimento, quindi il codice come 'reftype ($ x) eq 'HASH'' può causare avvisi. (ref, d'altra parte, restituisce convenientemente "" per non-riferimenti.) – ysth

+0

@ysth: abbastanza così! Ho aggiornato la mia risposta. È raro trovare un buon uso di 'ref' su oggetti benedetti. – Ether

14

Uno scalare contiene sempre un singolo elemento. Qualunque cosa sia in una variabile scalare è sempre uno scalare. Un riferimento è un valore scalare.

Se si desidera sapere se si tratta di un riferimento, è possibile utilizzare ref. Se si desidera conoscere il tipo di riferimento, , è possibile utilizzare la routine reftype da Scalar::Util.

Se si desidera sapere se si tratta di un oggetto, è possibile utilizzare la routine blessed da Scalar::Util. Non dovresti mai preoccuparti di quale sia il pacchetto benedetto, però. UNIVERSAL ha alcuni metodi per dirti di un oggetto: se vuoi verificare che abbia il metodo che vuoi chiamare, usa can; se vuoi vedere che eredita da qualcosa, usa isa; e se vuoi vederlo, l'oggetto gestisce un ruolo, usa DOES.

Se si desidera sapere se lo scalare si comporta semplicemente come uno scalare ma è collegato a una classe, provare a tied.Se ottieni un oggetto, continua i tuoi assegni.

Se si desidera sapere se assomiglia ad un numero, è possibile utilizzare looks_like_number da Scalar::Util. Se non sembra un numero e non è un riferimento, è una stringa. Tuttavia, tutti i valori semplici possono essere stringhe.

Se avete bisogno di fare qualcosa di più di fantasia, è possibile utilizzare un modulo come il Params::Validate.

2

A un certo punto ho letto un argomento ragionevolmente convincente su Perlmonks che testare il tipo di uno scalare con ref o reftype è una cattiva idea. Non ricordo chi ha lanciato l'idea o il link. Scusate.

Il punto era che in Perl ci sono molti meccanismi che consentono di fare un determinato atto scalare come quasi tutto quello che vuoi. Se si utilizza il filehandle tie in modo che funzioni come un hash, il test con reftype indicherà che si dispone di un file. Non ti dirà che devi usarlo come un hash.

Quindi, l'argomento è andato, è meglio usare la tipizzazione anatra per scoprire che cosa è una variabile.

Invece di:

sub foo { 
    my $var = shift; 
    my $type = reftype $var; 

    my $result; 
    if($type eq 'HASH') { 
     $result = $var->{foo}; 
    } 
    elsif($type eq 'ARRAY') { 
     $result = $var->[3]; 
    } 
    else { 
     $result = 'foo'; 
    } 

    return $result; 
} 

Si dovrebbe fare qualcosa di simile:

sub foo { 
    my $var = shift; 
    my $type = reftype $var; 

    my $result; 

    eval { 
     $result = $var->{foo}; 
     1; # guarantee a true result if code works. 
    } 
    or eval { 
     $result = $var->[3]; 
     1; 
    } 
    or do { 
     $result = 'foo'; 
    } 

    return $result; 
} 

Per la maggior parte io in realtà non faccio questo, ma in alcuni casi che ho. Sto ancora pensando a quando questo approccio è appropriato. Ho pensato di buttare fuori il concetto per ulteriori discussioni. Mi piacerebbe vedere i commenti.

Aggiornamento

mi sono reso conto che dovrei mettere avanti il ​​mio pensiero su questo approccio.

Questo metodo ha il vantaggio di gestire qualsiasi cosa ti buttare a questo.

ha lo svantaggio di essere ingombrante, e un po 'strano. Inciampare su questo in qualche codice mi farebbe emettere un grosso "WTF".

Mi piace l'idea di testare se uno scalare agisce come un hash-ref, piuttosto che se si tratta di un ref hash.

Non mi piace questa implementazione.

+4

Sono probabilmente quello a cui stai pensando. Dico mai non provare contro stringhe letterali. Prova contro i prototipi: if (ref $ f eq ref {}). Per quanto riguarda il pareggio, inizi con tied(): se ottieni un oggetto, fai le cose normali. –

+2

Per favore, mai, mai effettivamente testare i ref in questo modo. È così facile farlo più facilmente. :) –

+0

cervello, questo è un altro punto interessante - non quello a cui stavo pensando. Posso vedere il tuo punto però. – daotoad

4

mi piace il polimorfismo invece di controllare manualmente per qualcosa:

use MooseX::Declare; 

class Foo { 
    use MooseX::MultiMethods; 

    multi method foo (ArrayRef $arg){ say "arg is an array" } 
    multi method foo (HashRef $arg) { say "arg is a hash" } 
    multi method foo (Any $arg)  { say "arg is something else" } 
} 

Foo->new->foo([]); # arg is an array 
Foo->new->foo(40); # arg is something else 

Questo è molto più potente di controllo manuale, come si può riutilizzare i "controlli", come si farebbe con qualsiasi altro tipo di vincolo. Ciò significa che quando si desidera gestire matrici, hash e numeri pari a meno di 42, basta scrivere un vincolo per "numeri pari a meno di 42" e aggiungere un nuovo multimetodo per quel caso. Il "codice chiamante" non è interessato.

la libreria dei tipi:

package MyApp::Types; 
use MooseX::Types -declare => ['EvenNumberLessThan42']; 
use MooseX::Types::Moose qw(Num); 

subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 }; 

poi fare Foo supporta questa (in quella definizione di classe):

class Foo { 
    use MyApp::Types qw(EvenNumberLessThan42); 

    multi method foo (EvenNumberLessThan42 $arg) { say "arg is an even number less than 42" } 
} 

Poi Foo->new->foo(40) stampe arg is an even number less than 42 invece di arg is something else.

Manutenibile.

Problemi correlati