2009-11-13 2 views

Antwort

44

ref():

Perl die ref() Funktion bietet, so dass Sie den Referenztyp überprüfen kann, bevor eine Referenz dereferencing ...

Durch die ref() Funktion verwenden, können Sie Programmcode zu schützen, die Variablen aus dereferenziert Fehler erzeugen, wenn der falsche Referenztyp verwendet wird ...

+1

Ich dachte ref() würde Ihnen nur sagen, welche Art von Referenz es ist und nichts zurückgeben, wenn es nicht eins ist. –

+3

thx - nun, ich wollte nur thx sagen - aber es ist nicht erlaubt – pm100

+7

@Chris: Richtig, also, wenn die Variable keine Referenz ist, können Sie daraus schließen, dass es ein einfacher Skalar aus der Tatsache ist, dass es nichts zurückgibt. Andernfalls werden Sie wissen, um was für eine Referenz es sich handelt. –

40

$x ist immer ein Skalar. Der Hinweis ist das Sigil $: jede Variable (oder Dereferenzierung eines anderen Typs) beginnend mit $ ist ein Skalar. (Weitere Informationen zu Datentypen finden Sie unter perldoc perldata.)

Eine Referenz ist nur eine bestimmte Art von Skalar. Die eingebaute Funktion ref wird Ihnen sagen, um was für eine Referenz es sich handelt. Auf der anderen Seite, wenn Sie eine gesegnete Referenz haben, wird ref Ihnen nur den Paketnamen mitteilen, in den die Referenz gesegnet wurde, nicht den eigentlichen Kerntyp der Daten (gesegnete Referenzen können Hashrezepte, Arrayrefs oder andere Dinge sein). Sie können Scalar::Util ‚s verwenden reftype wird Ihnen sagen, welche Art von Referenz es ist:

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"; 

... erzeugt die Ausgabe:

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

Weitere Informationen zu den anderen Arten von Referenzen (zB Codereferenz , arrayref etc), siehe folgende Frage: How can I get Perl's ref() function to return REF, IO, and LVALUE? und perldoc perlref.

Hinweis: Sie sollten nicht Verwendung ref Code Zweige mit einem gesegneten Objekt zu implementieren (zB $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) - wenn Sie irgendwelche Entscheidungen über die Art einer variablen Basis vornehmen müssen, verwenden isa (dh if ($a->isa("My::Foo") { ... oder if ($a->can("foo") { ...) . Siehe auch polymorphism.

+3

Beachten Sie, dass reftype per Definition die Kapselung verletzt, also sollte vermieden werden, es sei denn, Sie haben einen sehr guten Grund. – ysth

+2

Wenn Sie reftype verwenden, beachten Sie, dass es undef für eine Nichtreferenz zurückgibt, so dass Code wie 'reftype ($ x) eq 'HASH'' zu Warnungen führen kann. (ref, auf der anderen Seite, gibt bequem '' für Non-Referenzen.) – ysth

+0

@ysth: Ganz so! Ich habe meine Antwort aktualisiert .. es ist selten, eine gute Verwendung für 'ref' auf gesegneten Objekten zu finden. – Ether

14

Ein Skalar enthält immer ein einzelnes Element. Was immer in einer skalaren Variablen ist, ist immer ein Skalar. Eine Referenz ist ein Skalarwert.

Wenn Sie wissen wollen, ob es sich um eine Referenz ist, können Sie ref verwenden. Wenn Sie den Referenztyp kennen möchten, können Sie die reftype Routine von Scalar::Util verwenden. Wenn Sie wissen wollen, ob es sich um ein Objekt handelt, können Sie die blessed Routine von Scalar::Util verwenden. Sie sollten sich jedoch nie darum kümmern, was das gesegnete Paket ist. UNIVERSAL hat einige Methoden, um Sie über ein Objekt zu informieren: Wenn Sie überprüfen möchten, dass es die Methode hat, die Sie aufrufen möchten, verwenden Sie can; Wenn Sie sehen möchten, dass es von etwas erbt, verwenden Sie isa; Wenn Sie eine Rolle sehen möchten, verwenden Sie DOES.

Wenn Sie wissen möchten, ob dieser Skalar sich tatsächlich wie ein Skalar verhält, aber an eine Klasse gebunden ist, versuchen Sie tied.Wenn Sie ein Objekt erhalten, setzen Sie Ihre Prüfungen fort. Wenn Sie wissen möchten, ob es wie eine Nummer aussieht, können Sie looks_like_number von Scalar::Util verwenden. Wenn es nicht wie eine Zahl aussieht und es sich nicht um eine Referenz handelt, handelt es sich um eine Zeichenfolge. Alle einfachen Werte können jedoch Zeichenfolgen sein.

Wenn Sie etwas ausgefalleneres tun müssen, können Sie ein Modul wie Params::Validate verwenden.

2

Irgendwann las ich ein einigermaßen überzeugendes Argument auf Perlmonks, dass das Testen des Typs eines Skalars mit ref oder reftype eine schlechte Idee ist. Ich weiß nicht mehr, wer die Idee vorangebracht hat oder die Verbindung. Es tut uns leid.

Der Punkt war, dass es in Perl viele Mechanismen gibt, die es möglich machen, dass ein gegebener Skalar sich so verhält, als würde er alles tun, was man will. Wenn Sie tie ein Dateihandle, so dass es sich wie ein Hash verhält, wird der Test mit reftype Ihnen sagen, dass Sie eine Datei haben. Es wird Ihnen nicht sagen, dass Sie es wie ein Hash verwenden müssen.

Also ging das Argument, es ist besser, Ente Typisierung zu verwenden, um herauszufinden, was eine Variable ist.

Statt:

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; 
} 

Sie sollten etwas tun:

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; 
} 

Für den größten Teil dieser ich eigentlich gar nicht tun, aber in einigen Fällen die ich habe. Ich bin immer noch dabei, zu überlegen, wann dieser Ansatz angemessen ist. Ich dachte, ich würde das Konzept zur weiteren Diskussion rauswerfen. Ich würde gerne Kommentare sehen.

aktualisieren

ich, dass ich auf diesem Ansatz meine Gedanken vorbringen realisiert sollte.

Diese Methode hat den Vorteil, alles zu handhaben, was Sie darauf werfen.

Es hat den Nachteil, umständlich und etwas seltsam. Wenn ich in einem Code darüber stolpere, würde ich einen großen fetten WTF machen.

Ich mag die Idee zu testen, ob ein Skalar wirkt wie ein Hash-Ref, eher, ob es ein Hash-Ref ist.

Ich mag diese Implementierung nicht.

+4

Ich bin wahrscheinlich derjenige, an den du denkst. Ich sage, niemals gegen wörtliche Zeichenfolgen testen. Test gegen Prototypen: if (ref $ f eq ref {}). Wie bei Krawatte, beginnen Sie mit gebundenem(): Wenn Sie ein Objekt bekommen, tun Sie das normale Zeug. –

+2

Bitte nie, nie tatsächlich Tests Refs so. Es ist so einfach, es einfacher zu machen. :) –

+0

Gehirn, das ist ein weiterer interessanter Punkt - nicht der, an den ich dachte. Ich kann deinen Standpunkt jedoch sehen. – daotoad

4

Ich mag Polymorphismus statt manuelle Überprüfung nach etwas:

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 

Das ist viel mächtiger als die manuelle Überprüfung, wie Sie Ihre „checks“ wie würden Sie eine andere Art Zwang wiederverwenden können. Das bedeutet, wenn Sie mit Arrays, Hashes und geraden Zahlen von weniger als 42 umgehen wollen, schreiben Sie einfach eine Beschränkung für "gerade Zahlen kleiner als 42" und fügen eine neue Multimethode für diesen Fall hinzu. Der "Rufcode" ist nicht betroffen.

Ihre Bibliothek Typ:

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

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

Dann machen Foo diese (in dieser Klassendefinition) unterstützen:

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

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

Dann Foo->new->foo(40) druckt arg is an even number less than 42 statt arg is something else.

Wartbar.

Verwandte Themen