2010-08-04 14 views
12

Mein Ziel ist es, $obj verwenden wie folgt aus:Perl: Wie erstellt man Objekte im laufenden Betrieb?

print $obj->hello() . $obj->{foo}; 

Und ich möchte ein Objekt inline erstellen, vielleicht etwas mit wie dies:

my $obj = (
    foo => 1, 
    hello => sub { return 'world' } 
); 

aber wenn ich versuche, Um $obj als ein Objekt zu verwenden, erhalte ich eine Fehlermeldung, dass $ obj nicht gesegnet wurde. Gibt es eine Basisklasse (wie stdClass in PHP), die ich verwenden kann, um den Hash zu segnen, so dass ich es als ein Objekt verwenden kann?


Für diejenigen, die JavaScript wissen, ich versuche, die folgende zu tun, aber in Perl:

# JS CODE BELOW 
var obj = { foo: 1, hello: function() { return 'world' } }; 
echo obj.hello() + obj.foo; 
+0

Perl Methoden nicht Felder in einem Hash sind - das ist nicht Java. – Ether

+0

JavaScript ist auch nicht Java. – cjm

Antwort

14

Perl würde ein wenig Hilfe benötigen, dies zu tun. Weil Codenreferenzen, die in Hashes gespeichert sind, nicht als "Methoden" betrachtet werden. Methoden werden als Einträge in eine Package-Symboltabelle implementiert.Perl ist mehr Klasse-orientierte als JavaScript, die stolz verkündet, dass es mehr objektorientierte (auf einzelne Objekte) ist.

Um diese Funktionalität ausführen zu können, müssten Sie eine Klasse erstellen, die Referenzen auf diese Weise zuordnet. Der Weg, Methoden in der Symboltabelle zu umgehen, ist die AUTOLOAD Methode. Wenn ein Paket eine AUTOLOAD Unterroutine enthält, wird ein Aufruf an ein gesegnetes Objekt, das Perl in der Vererbungskette nicht finden kann, AUTOLOAD aufrufen und die paketspezifische (our) Variable setzen $AUTOLOAD wird die volle Name der sein Funktion.

Wir erhalten den Namen der aufgerufenen Methode, indem wir den letzten Knoten (nach dem letzten '::') des voll qualifizierten Subnamens abrufen. Wir prüfen, ob dort eine Codeferenz vorhanden ist, und falls ja, können wir sie zurückgeben.

package AutoObject; 

use strict; 
use warnings; 
use Carp; 
use Params::Util qw<_CODE>; 
our $AUTOLOAD; 

sub AUTOLOAD { 
    my $method_name = substr($AUTOLOAD, index($AUTOLOAD, '::') + 2); 
    my ($self) = @_; 
    my $meth  = _CODE($self->{$method_name}); 
    unless ($meth) { 
     Carp::croak("object does not support method='$method_name'!"); 
    } 
    goto &$meth; 
} 


1; 

Dann würden Sie das Objekt in dieser Klasse segnen:

package main; 

my $obj 
    = bless { foo => 1 
     , hello => sub { return 'world' } 
     }, 'AutoObject'; 

print $obj->hello(); 

Normalerweise in einem AUTOLOAD Unter I "Zement" Verhalten. Das heißt, ich erstelle Einträge in die Paketsymboltabelle, um das nächste Mal AUTOLOAD zu vermeiden. Aber das ist normalerweise für ein vernünftig definiertes Klassenverhalten.

Ich entwarf auch eine QuickClass, die ein Paket für jedes deklarierte Objekt erstellt, aber das eine Menge von Symboltabellenkonflikten enthält, die jetzt Tage wahrscheinlich besser mit Class::MOP erledigt werden.


den Vorschlag von Eric Strom gegeben, können Sie den folgenden Code in das AutoObject Paket hinzuzufügen. Der import Sub würde jederzeit jemand heißen use -d AutoObject (mit dem Parameter 'object').

# Definition: 
sub object ($) { return bless $_[0], __PACKAGE__; }; 

sub import { # gets called when Perl reads 'use AutoObject;' 
    shift; # my name 
    return unless $_[0] eq 'object'; # object is it's only export 
    use Symbol; 
    *{ Symbol::qualify_to_reference('object', scalar caller()) } 
     = \&object 
     ; 
} 

Und dann, wenn Sie ein "Objektliteral" erstellen möchten, können Sie einfach tun:

use AutoObject qw<object>; 

Und der Ausdruck wäre:

object { foo => 1, hello => sub { return 'world' } }; 

Man könnte sogar tun :

object { name => 'World' 
     , hello => sub { return "Hello, $_[0]->{name}"; } 
     }->hello() 
     ; 

Und Sie haben ein "Objekt wörtlicher "Ausdruck. Vielleicht würde das Modul besser Object::Literal heißen.

+0

Dies ist eigentlich ein handlicher Trick. Das könnte ich gut gebrauchen, wenn ich jemals OOP in Perl benutzt habe. –

+0

Sie könnten den aufrufenden Code etwas sauberer machen, indem Sie den Segen in einem sub: 'sub Objekt {bless $ _ [0] => 'AutoObject'}' und dann 'my $ obj = object {...};' verstecken –

+0

@Eric Strom: das macht Sinn. Aber hier ist mein Denken: Das JavaScript-Idiom ist im Grunde ein "Objektliteral". In Perl ist das "Objektliteral" (oft) "segne {...}, 'MyClass'". * Natürlich *, da 'bless' nur ein Func ist, gibt es keinen Grund, dass' object {...} 'kein besseres Literal ist. Ich werde meinen Code ändern. – Axeman

2

$obj würde ein Skalar sein, so was auch immer Sie zuweisen, um es ein Skalar sein muss als Gut. Sie könnten entweder sagen

my %obj = (foo => 1, hello => sub { return 'world' }); 

oder

my $obj = { foo => 1, hello => sub { return 'world' }}; 

Letztere mit den geschweiften Klammern, eine Hash-Referenz erzeugt (das ist ein Skalar ist, so kann es gehen in $obj). Um zu den Inhalten innerhalb einer Hash-Referenz zu gelangen, müssen Sie jedoch den Pfeiloperator verwenden. Etwas wie $obj->{foo} oder &{$obj->{hello}}.

Wenn Sie keine Listen mit Hashes oder ähnlichem benötigen, ist es im Allgemeinen besser, die erste Methode zu verwenden.

In beiden Fällen können Sie nicht $obj->hello() sagen. Perl verwendet diese Syntax für seinen eigenen Geschmack von OOP, der die hello-Funktion in einem separaten Paket hat, das in Ihre Referenz bless ed eingefügt wurde. Wie:

package example; 
sub new {} { my $result = {}; return bless $result, 'example' } 
sub hello { return 'world' } 

package main; 
my $obj = example->new(); 

Wie Sie sehen können, sind die Methoden, die Sie aufrufen können bereits definiert, und es ist nicht trivial mehr hinzuzufügen. Es gibt magische Methoden, mit denen man so etwas machen kann, aber es lohnt sich wirklich nicht. &{$obj{hello}} (oder &{$obj->{hello}} für eine Referenz) ist weniger Aufwand als zu versuchen, Perl wie Javascript funktionieren zu lassen.

+0

Nun, das gibt mir Lesestoff für den Rest des Tages ... – Tom

0

Methoden in Perl sind keine Eigenschaften des Objekts wie in Python. Methoden sind einfache reguläre Funktionen Funktionen in einem Paket mit dem Objekt verbunden sind. Reguläre Funktionen nehmen ein zusätzliches Argument für die Selbstreferenz.

Sie können keine dynamisch erstellten Funktionen als Methoden verwenden. Hier

ist ein Zitat von perldoc perlobj:

1. An object is simply a reference that happens to know which class it 
     belongs to. 

    2. A class is simply a package that happens to provide methods to deal 
     with object references. 

    3. A method is simply a subroutine that expects an object reference 
     (or a package name, for class methods) as the first argument. 

Oh, und segnet() ist, wie Sie die Verbindung zwischen dem Referenz- und dem Paket zu etablieren.

+0

Nicht sicher, was Sie im Sinn haben, indem Sie sagen: * "Sie können nicht dynamisch Funktionen als Methoden erstellt haben." * Man kann Funktionen dynamisch generieren und einfügen Ein Packet. Viele CPAN-Module tun dies. Als ziemlich einfaches Beispiel siehe Getopt :: Long :: Deskriptive. Für jeden Optionsparser, der vom Benutzer angefordert wird, generiert er einen neuen Klassennamen. In diesem Paket wird ein Getter für jede vom Benutzer definierte Befehlszeilenoption installiert. – FMc

+0

Ich meine, dass Sie eine Methode nicht so hinzufügen können, wie er es möchte. Die dynamische Funktionseinfügung ist viel raffinierter als diese ("kein striktes Ref; * {" $ package \ :: function "} = sub {...};" irgendjemand? Gah!) Und sollte nicht leichtfertig durchgeführt werden. – Arkadiy

1

In welcher Funktion Sie das Objekt auch erstellen, müssen Sie bless auf Ihrem Objekt aufrufen, um Methodenaufruf zu aktivieren.

Zum Beispiel:

package MyClass; 

sub new 
{ 
    my $obj = { 
    foo => 1 
    }; 

    return bless($obj, "MyClass"); 
} 

sub hello 
{ 
    my $self = shift; 
    # Do stuff, including shifting off other arguments if needed 
} 

package main; 
my $obj = MyClass::new(); 

print "Foo: " . $obj->{foo} . "\n"; 
$obj->hello(); 

EDIT: Wenn Sie in der Lage sein wollen, für Ihre Objekte dynamische Funktionalität bereitzustellen Unterprogramm Referenzen zu verwenden ...

Erstens können Sie Ihre Referenz-Code wie so erstellen (in diesem Hash-Konstruktor Beispiel):

my $obj = { 
    foo => 1, 
    hello => sub { print "Hello\n"; }, 
} 

Sie können es wie folgt dann aufrufen:

my $obj = MyClass::new(); # or whatever 
$obj->{hello}->(@myArguments); 

ein wenig umständlich, aber es funktioniert. (Sie könnten nicht einmal den zweiten Pfeil brauchen, aber ich bin mir nicht sicher.)

2

Es Dinkel ist ein bisschen anders in Perl:

my $obj = { foo => 1, hello => sub { return "world" } }; 
print $obj->{hello}() . $obj->{foo}; 

Aber der Code ist umständlich. Die Warnung, die Sie über die nicht gesegnete Referenz erhalten haben, sagt Ihnen, dass Ihre Objekte nicht in the way Perl expects implementiert sind. Der Operator bless markiert ein Objekt mit dem Paket, in dem mit der Suche nach seinen Methoden begonnen werden soll.

Sagen Sie uns, was Sie in Bezug auf Ihre Problemdomäne tun möchten, und wir können Vorschläge für einen natürlicheren Ansatz in Perl machen.

4

Ein weiterer Perlish-Ansatz besteht darin, einen separaten Namespace für die gewünschten Methoden Ihres Objekts und bless das Objekt zu erstellen, um diese Methoden für Ihr Objekt verfügbar zu machen. Der Code dafür kann immer noch sehr gut sein.

my $obj = bless { foo => 1 }, "bar"; 
sub bar::hello { return 'world' }; 

Wie gbacon schon sagt, wenn Sie bereit sind $obj->{hello}->() zu schreiben statt $obj->hello(), können Sie den Vorgang segnen überspringen.

my $obj = { foo => 1, hello => sub { return 'world' } }; 
0

Ich empfehle die Verwendung von Class :: Struct wie in perltoot man-Seite erklärt.

Statt die Dokumentation zu paraphrasieren, lassen Sie es mich zitieren, wie es gut erklärt, wie das funktioniert:

„Was sie tut, ist, dass Sie bieten eine Möglichkeit, zu‚erklären‘, um eine Klasse, die als Objekte, deren Felder eines bestimmten mit Die Funktion, die dies tut, wird, erstaunlicherweise, struct() aufgerufen. Da Strukturen oder Datensätze in Perl keine Basistypen sind, müssen Sie jedes Mal, wenn Sie eine Klasse erstellen wollen, um ein datensatzartiges Datenobjekt bereitzustellen Definieren Sie eine neue() -Methode und separate Datenzugriffsmethoden für jedes Feld dieses Datensatzes. Sie werden schnell mit diesem Prozess gelangweilt werden. Die Class :: Struct :: struct() - Funktion erleichtert diese Langeweile. "

Still aus dem doc zitiert ein Beispiel Art und Weise, wie es zu implementieren:

use Class::Struct qw(struct); 
use Jobbie; # user-defined; see below 
struct 'Fred' => { 
    one  => '$', 
    many  => '@', 
    profession => 'Jobbie', # does not call Jobbie->new() 
}; 
$ob = Fred->new(profession => Jobbie->new()); 
$ob->one("hmmmm"); 
$ob->many(0, "here"); 
$ob->many(1, "you"); 
$ob->many(2, "go"); 
print "Just set: ", $ob->many(2), "\n"; 
$ob->profession->salary(10_000); 
Verwandte Themen