Ich verstehe, dass man das Schlüsselwort "segne" in Perl in der "neuen" Methode einer Klasse verwendet:
sub new {
my $self = bless { };
return $self;
}
Aber was genau macht "segne" mit dieser Hash-Referenz?
Ich verstehe, dass man das Schlüsselwort "segne" in Perl in der "neuen" Methode einer Klasse verwendet:
sub new {
my $self = bless { };
return $self;
}
Aber was genau macht "segne" mit dieser Hash-Referenz?
Antworten:
bless
Ordnet ein Objekt im Allgemeinen einer Klasse zu.
package MyClass;
my $object = { };
bless $object, "MyClass";
Wenn Sie nun eine Methode aufrufen $object
, weiß Perl, welches Paket nach der Methode gesucht werden soll.
Wenn das zweite Argument wie in Ihrem Beispiel weggelassen wird, wird das aktuelle Paket / die aktuelle Klasse verwendet.
Aus Gründen der Klarheit könnte Ihr Beispiel wie folgt geschrieben sein:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
EDIT: Weitere Informationen finden Sie in der guten Antwort von kixx .
bless
ordnet einem Paket eine Referenz zu.
Es spielt keine Rolle, worauf sich die Referenz bezieht, es kann sich um einen Hash (häufigster Fall), ein Array (nicht so häufig), einen Skalar (normalerweise zeigt dies ein Inside-Out-Objekt an ) oder einen regulären Ausdruck handeln , Unterprogramm oder TYPEGLOB ( nützliche Beispiele finden Sie im Buch Objektorientiertes Perl: Ein umfassender Leitfaden zu Konzepten und Programmiertechniken von Damian Conway ) oder sogar einen Verweis auf ein Datei- oder Verzeichnishandle (am wenigsten verbreiteter Fall).
Der Effekt bless
besteht darin, dass Sie eine spezielle Syntax auf die gesegnete Referenz anwenden können.
Wenn beispielsweise eine gesegnete Referenz in gespeichert ist $obj
(verbunden bless
mit dem Paket "Klasse"), $obj->foo(@args)
ruft sie eine Unterroutine auf foo
und übergibt als erstes Argument die Referenz, $obj
gefolgt von den restlichen Argumenten ( @args
). Das Unterprogramm sollte im Paket "Klasse" definiert sein. Wenn das foo
Paket "Klasse" keine Unterroutine enthält , wird eine Liste anderer Pakete (aus dem Array @ISA
im Paket "Klasse" entnommen ) durchsucht und die erste foo
gefundene Unterroutine aufgerufen.
Diese Funktion teilt der von REF referenzierten Entität mit, dass es sich nun um ein Objekt im CLASSNAME-Paket handelt oder um das aktuelle Paket, wenn CLASSNAME weggelassen wird. Die Verwendung der Segenform mit zwei Argumenten wird empfohlen.
Beispiel :
bless REF, CLASSNAME
bless REF
Rückgabewert
Diese Funktion gibt den Verweis auf ein in CLASSNAME gesegnetes Objekt zurück.
Beispiel :
Im Folgenden finden Sie den Beispielcode, der die grundlegende Verwendung zeigt. Die Objektreferenz wird erstellt, indem eine Referenz auf die Klasse des Pakets gesegnet wird.
#!/usr/bin/perl
package Person;
sub new
{
my $class = shift;
my $self = {
_firstName => shift,
_lastName => shift,
_ssn => shift,
};
# Print all the values just for clarification.
print "First Name is $self->{_firstName}\n";
print "Last Name is $self->{_lastName}\n";
print "SSN is $self->{_ssn}\n";
bless $self, $class;
return $self;
}
Ich werde hier eine Antwort geben, da die hier nicht ganz für mich geklickt haben.
Perls Segensfunktion verknüpft jeden Verweis auf alle Funktionen innerhalb eines Pakets.
Warum brauchen wir das?
Beginnen wir mit einem Beispiel in JavaScript:
(() => {
'use strict';
class Animal {
constructor(args) {
this.name = args.name;
this.sound = args.sound;
}
}
/* [WRONG] (global scope corruption)
* var animal = Animal({
* 'name': 'Jeff',
* 'sound': 'bark'
* });
* console.log(animal.name + ', ' + animal.sound); // seems good
* console.log(window.name); // my window's name is Jeff?
*/
// new is important!
var animal = new Animal(
'name': 'Jeff',
'sound': 'bark'
);
console.log(animal.name + ', ' + animal.sound); // still fine.
console.log(window.name); // undefined
})();
Lassen Sie uns nun das Klassenkonstrukt entfernen und darauf verzichten:
(() => {
'use strict';
var Animal = function(args) {
this.name = args.name;
this.sound = args.sound;
return this; // implicit context hashmap
};
// the "new" causes the Animal to be unbound from global context, and
// rebinds it to an empty hash map before being constructed. The state is
// now bound to animal, not the global scope.
var animal = new Animal({
'name': 'Jeff',
'sound': 'bark'
});
console.log(animal.sound);
})();
Die Funktion verwendet eine Hash-Tabelle mit ungeordneten Eigenschaften (da es 2016 keinen Sinn macht, Eigenschaften in einer bestimmten Reihenfolge in dynamischen Sprachen schreiben zu müssen) und gibt eine Hash-Tabelle mit diesen Eigenschaften zurück, oder wenn Sie vergessen haben, das neue Schlüsselwort einzugeben gibt den gesamten globalen Kontext zurück (z. B. Fenster im Browser oder global in nodejs).
Perl hat weder "dies" noch "neu" noch "Klasse", kann aber dennoch eine Funktion haben, die sich ähnlich verhält. Wir werden weder einen Konstruktor noch einen Prototyp haben, aber wir werden in der Lage sein, nach Belieben neue Tiere zu erstellen und ihre individuellen Eigenschaften zu ändern.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Jetzt haben wir ein Problem: Was ist, wenn wir möchten, dass das Tier die Geräusche selbst ausführt, anstatt dass wir drucken, was ihre Stimme ist? Das heißt, wir möchten eine Funktion performSound, die den eigenen Klang des Tieres druckt.
Eine Möglichkeit, dies zu tun, besteht darin, jedem einzelnen Tier beizubringen, wie es seinen Klang erzeugt. Dies bedeutet, dass jede Katze ihre eigene Duplikatfunktion hat, um Sound auszuführen.
# self contained scope
(sub {
my $Animal = (sub {
$name = $_[0]{'name'};
$sound = $_[0]{'sound'};
return {
'name' => $name,
'sound' => $sound,
'performSound' => sub {
print $sound . "\n";
}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
$animal->{'performSound'}();
})->();
Dies ist schlecht, da performSound jedes Mal, wenn ein Tier konstruiert wird, als völlig neues Funktionsobjekt gesetzt wird. 10000 Tiere bedeuten 10000 performSounds. Wir möchten eine einzige Funktion performSound haben, die von allen Tieren verwendet wird, die ihren eigenen Sound nachschlagen und ihn drucken.
(() => {
'use strict';
/* a function that creates an Animal constructor which can be used to create animals */
var Animal = (() => {
/* function is important, as fat arrow does not have "this" and will not be bound to Animal. */
var InnerAnimal = function(args) {
this.name = args.name;
this.sound = args.sound;
};
/* defined once and all animals use the same single function call */
InnerAnimal.prototype.performSound = function() {
console.log(this.name);
};
return InnerAnimal;
})();
/* we're gonna create an animal with arguments in different order
because we want to be edgy. */
var animal = new Animal({
'sound': 'bark',
'name': 'Jeff'
});
animal.performSound(); // Jeff
})();
Hier hört die Parallele zu Perl auf.
Der neue Operator von JavaScript ist nicht optional. Ohne diesen Operator beschädigt "this" innerhalb der Objektmethoden den globalen Bereich:
(() => {
// 'use strict'; // uncommenting this prevents corruption and raises an error instead.
var Person = function() {
this.name = "Sam";
};
// var wrong = Person(); // oops! we have overwritten window.name or global.main.
// console.log(window.name); // my window's name is Sam?
var correct = new Person; // person's name is actually stored in the person now.
})();
Wir möchten eine Funktion für jedes Tier haben, die den eigenen Klang dieses Tieres nachschlägt, anstatt ihn beim Bau fest zu codieren.
Durch den Segen können wir ein Paket als Prototyp von Objekten verwenden. Auf diese Weise kennt das Objekt das "Paket", auf das es "referenziert" ist, und kann wiederum die Funktionen im Paket dazu bringen, in die spezifischen Instanzen zu "greifen", die vom Konstruktor dieses "Paketobjekts" erstellt wurden:
package Animal;
sub new {
my $packageRef = $_[0];
my $name = $_[1]->{'name'};
my $sound = $_[1]->{'sound'};
my $this = {
'name' => $name,
'sound' => $sound
};
bless($this, $packageRef);
return $this;
}
# all animals use the same performSound to look up their sound.
sub performSound {
my $this = shift;
my $sound = $this->{'sound'};
print $sound . "\n";
}
package main;
my $animal = Animal->new({
'name' => 'Cat',
'sound' => 'meow'
});
$animal->performSound();
Zusammenfassung / TL; DR :
Perl hat weder "dies", "Klasse" noch "neu". Wenn Sie ein Objekt für ein Paket segnen, erhält dieses Objekt einen Verweis auf das Paket. Wenn es Funktionen im Paket aufruft, werden ihre Argumente um 1 Slot versetzt, und das erste Argument ($ _ [0] oder Shift) entspricht Javascript ist "dies". Im Gegenzug können Sie das Prototypmodell von JavaScript etwas simulieren.
Leider ist es (nach meinem Verständnis) unmöglich, zur Laufzeit "neue Klassen" zu erstellen, da jede "Klasse" ein eigenes Paket haben muss, während Sie in Javascript überhaupt keine Pakete als "neues" Schlüsselwort benötigen erstellt eine anonyme Hashmap, die Sie zur Laufzeit als Paket verwenden können, zu der Sie im laufenden Betrieb neue Funktionen hinzufügen und Funktionen entfernen können.
Es gibt einige Perl-Bibliotheken, die ihre eigenen Methoden entwickeln, um diese Einschränkung der Ausdruckskraft zu überwinden, wie z. B. Moose.
Warum die Verwirrung? ::
Wegen Paketen. Unsere Intuition sagt uns, dass wir das Objekt an eine Hashmap binden sollen, die seinen Prototyp enthält. Auf diese Weise können wir zur Laufzeit "Pakete" erstellen, wie dies JavaScript kann. Perl verfügt nicht über eine solche Flexibilität (zumindest nicht eingebaut, Sie müssen es erfinden oder von anderen Modulen beziehen), und Ihre Laufzeit-Ausdruckskraft wird wiederum beeinträchtigt. Es "Segen" zu nennen, tut ihm auch nicht viel Gutes.
Was wir tun wollen :
So etwas, aber die Bindung an die Prototypkarte ist rekursiv und implizit an den Prototyp gebunden, anstatt dies explizit tun zu müssen.
Hier ist ein naiver Versuch: Das Problem ist, dass "call" nicht weiß, wie es heißt ", also kann es auch eine universelle Perl-Funktion" objectInvokeMethod (object, method) "sein, die prüft, ob das Objekt die Methode hat oder sein Prototyp hat es oder sein Prototyp hat es, bis es das Ende erreicht und es findet oder nicht (prototypische Vererbung). Perl hat eine schöne Eval-Magie, aber ich lasse das für etwas, das ich später versuchen kann.
Jedenfalls ist hier die Idee:
(sub {
my $Animal = (sub {
my $AnimalPrototype = {
'performSound' => sub {
return $_[0]->{'sound'};
}
};
my $call = sub {
my $this = $_[0];
my $proc = $_[1];
if (exists $this->{$proc}) {
return $this->{$proc}->();
} else {
return $this->{prototype}->{$proc}->($this, $proc);
}
};
return sub {
my $name = $_[0]->{name};
my $sound = $_[0]->{sound};
my $this = {
'this' => $this,
'name' => $name,
'sound' => $sound,
'prototype' => $AnimalPrototype,
'call' => $call
};
};
})->();
my $animal = $Animal->({
'name' => 'Jeff',
'sound'=> 'bark'
});
print($animal->{call}($animal, 'performSound'));
})->();
Wie auch immer, hoffentlich findet jemand diesen Beitrag nützlich.
my $o = bless {}, $anything;
wird ein Objekt in die $anything
Klasse segnen . In ähnlicher Weise {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
wird eine Methode mit dem Namen 'somesub' in der Klasse mit dem Namen 'somesub' erstellt $anything
. Dies ist alles zur Laufzeit möglich. "Möglich" macht es jedoch nicht zu einer großartigen Praxis, im täglichen Code zu arbeiten. Es ist jedoch nützlich beim Erstellen von Objektüberlagerungssystemen wie Moose oder Moo.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
Anspruch ungültig . Ich denke, meine Besorgnis hat sich letztendlich darauf beschränkt, dass es wesentlich weniger intuitiv ist, das Paketsystem zur Laufzeit zu manipulieren / zu überprüfen, aber bisher habe ich nichts gezeigt, was es von Natur aus nicht kann. Das Paketsystem scheint alle Tools zu unterstützen, die zum Hinzufügen / Entfernen / Prüfen / Ändern zur Laufzeit erforderlich sind.
Zusammen mit einer Reihe von guten Antworten zeichnet sich eine bless
-ed-Referenz besonders dadurch aus, dass die SV
dafür ein zusätzliches FLAGS
( OBJECT
) und einSTASH
perl -MDevel::Peek -wE'
package Pack { sub func { return { a=>1 } } };
package Class { sub new { return bless { A=>10 } } };
$vp = Pack::func(); print Dump $vp; say"---";
$obj = Class->new; print Dump $obj'
Drucke, bei denen dieselben (und hierfür irrelevanten) Teile unterdrückt werden
SV = IV (0x12d5530) bei 0x12d5540 REFCNT = 1 FLAGGEN = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) bei 0x12a5a68 REFCNT = 1 FLAGS = (SHAREKEYS) ... SV = IV (0x12a5ce0) bei 0x12a5cf0 REFCNT = 1 FLAGS = (IOK, pIOK) IV = 1 --- ---. SV = IV (0x12cb8b8) bei 0x12cb8c8 REFCNT = 1 FLAGGEN = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) bei 0x12c26b0 REFCNT = 1 FLAGS = (OBJECT, SHAREKEYS) STASH = 0x12d5300 "Klasse" ... SV = IV (0x12c26b8) bei 0x12c26c8 REFCNT = 1 FLAGS = (IOK, pIOK) IV = 10
Damit ist bekannt, dass 1) es ein Objekt ist 2) zu welchem Paket es gehört, und dies informiert über seine Verwendung.
Wenn beispielsweise eine Dereferenzierung auf diese Variable auftritt ( $obj->name
), wird im Paket (oder in der Hierarchie) ein Sub mit diesem Namen gesucht, das Objekt wird als erstes Argument übergeben usw.
I Diesem Gedanken folgend, um die Entwicklung objektorientierter Perl zu leiten.
Bless ordnet eine Datenstrukturreferenz einer Klasse zu. Angesichts der Art und Weise, wie Perl die Vererbungsstruktur (in einer Art Baum) erstellt, ist es einfach, das Objektmodell zu nutzen, um Objekte für die Komposition zu erstellen.
Für diese Assoziation haben wir Objekt genannt, um zu entwickeln, immer daran zu denken, dass der interne Zustand des Objekts und das Klassenverhalten getrennt sind. Und Sie können jede Datenreferenz segnen / zulassen, um jedes Paket- / Klassenverhalten zu verwenden. Da das Paket "den emotionalen" Zustand des Objekts verstehen kann.
Wenn Sie beispielsweise sicher sein können, dass ein Bug-Objekt ein gesegneter Hash sein wird, können Sie (endlich!) Den fehlenden Code in der Bug :: print_me-Methode eingeben:
package Bug;
sub print_me
{
my ($self) = @_;
print "ID: $self->{id}\n";
print "$self->{descr}\n";
print "(Note: problem is fatal)\n" if $self->{type} eq "fatal";
}
Wenn nun die print_me-Methode über eine Referenz auf einen Hash aufgerufen wird, der in die Bug-Klasse aufgenommen wurde, extrahiert die Variable $ self die Referenz, die als erstes Argument übergeben wurde, und die print-Anweisungen greifen dann auf die verschiedenen Einträge des gesegneten Hash zu.