Capisco che uno usa la parola chiave "benedica" in Perl all'interno del metodo "nuovo" di una classe:
sub new {
my $self = bless { };
return $self;
}
Ma cosa sta facendo esattamente "benedici" per quel riferimento all'hash?
Capisco che uno usa la parola chiave "benedica" in Perl all'interno del metodo "nuovo" di una classe:
sub new {
my $self = bless { };
return $self;
}
Ma cosa sta facendo esattamente "benedici" per quel riferimento all'hash?
Risposte:
In generale, bless
associa un oggetto a una classe.
package MyClass;
my $object = { };
bless $object, "MyClass";
Ora, quando invochi un metodo $object
, Perl sa quale pacchetto cercare il metodo.
Se il secondo argomento viene omesso, come nell'esempio, viene utilizzato il pacchetto / classe corrente.
Per motivi di chiarezza, il tuo esempio potrebbe essere scritto come segue:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
EDIT: vedi la buona risposta di Kixx per un po 'più di dettagli.
bless
associa un riferimento a un pacchetto.
Non importa quale sia il riferimento, può essere un hash (caso più comune), un array (non così comune), uno scalare (di solito questo indica un oggetto dentro-fuori ), un'espressione regolare , subroutine o TYPEGLOB (vedere il libro Perl orientato agli oggetti: una guida completa ai concetti e alle tecniche di programmazione di Damian Conway per esempi utili) o persino un riferimento a un handle di file o directory (caso meno comune).
L'effetto bless
ha che ti permette di applicare una sintassi speciale al riferimento benedetto.
Ad esempio, se un riferimento benedetto è memorizzato in $obj
(associato bless
al pacchetto "Class"), allora $obj->foo(@args)
chiamerà una subroutine foo
e passerà come primo argomento il riferimento $obj
seguito dal resto degli argomenti ( @args
). La subroutine deve essere definita nel pacchetto "Class". Se non è presente alcuna subroutine foo
nel pacchetto "Class", @ISA
verrà cercato un elenco di altri pacchetti (preso dall'array nel pacchetto "Class") e foo
verrà chiamata la prima subroutine trovata.
Questa funzione indica all'entità a cui fa riferimento REF che ora è un oggetto nel pacchetto CLASSNAME o il pacchetto corrente se CLASSNAME viene omesso. Si raccomanda l'uso della forma a due argomenti della benedizione.
Esempio :
bless REF, CLASSNAME
bless REF
Valore di ritorno
Questa funzione restituisce il riferimento a un oggetto benedetto in CLASSNAME.
Esempio :
Di seguito è riportato il codice di esempio che mostra il suo utilizzo di base, il riferimento all'oggetto viene creato benedendo un riferimento alla classe del pacchetto -
#!/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;
}
Fornirò una risposta qui poiché quelli qui non mi hanno cliccato.
La funzione benedica di Perl associa qualsiasi riferimento a tutte le funzioni all'interno di un pacchetto.
Perché dovremmo averne bisogno?
Cominciamo esprimendo un esempio 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
})();
Ora consente di eliminare il costrutto di classe e di farne a meno:
(() => {
'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);
})();
La funzione accetta una tabella hash di proprietà non ordinate (dal momento che non ha senso scrivere proprietà in un ordine specifico in linguaggi dinamici nel 2016) e restituisce una tabella hash con quelle proprietà, o se hai dimenticato di inserire la nuova parola chiave, restituirà l'intero contesto globale (ad es. finestra nel browser o globale in nodejs).
Perl non ha "questo" né "nuovo" né "classe", ma può comunque avere una funzione che si comporta in modo simile. Non avremo un costruttore né un prototipo, ma saremo in grado di creare nuovi animali a volontà e modificare le loro proprietà individuali.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Ora, abbiamo un problema: che cosa succede se vogliamo che l'animale esegua i suoni da soli invece di stampare la loro voce. Cioè, vogliamo una funzione performSound che stampa il suono dell'animale.
Un modo per farlo è insegnare a ogni singolo animale come fare il suono. Ciò significa che ogni gatto ha la sua funzione duplicata per eseguire SoundSound.
# 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'}();
})->();
Ciò è negativo perché performSound viene inserito come un oggetto funzione completamente nuovo ogni volta che viene costruito un animale. 10000 animali significa 10000 performSounds. Vogliamo avere una sola funzione performSound che viene utilizzata da tutti gli animali che cerca il proprio suono e lo stampa.
(() => {
'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
})();
Qui è dove si ferma il parallelo a Perl kinda.
Il nuovo operatore JavaScript non è facoltativo, senza di esso, "questo" all'interno dei metodi degli oggetti corrompe l'ambito globale:
(() => {
// '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.
})();
Vogliamo avere una funzione per ogni Animale che cerca il suono di quell'animale piuttosto che codificarlo alla costruzione.
La benedizione ci consente di utilizzare un pacchetto come prototipo di oggetti. In questo modo, l'oggetto è a conoscenza del "pacchetto" a cui "fa riferimento" e, a sua volta, può avere le funzioni nel pacchetto "raggiungere" le istanze specifiche che sono state create dal costruttore di tale "oggetto pacchetto":
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();
Riepilogo / TL; DR :
Perl non ha "questo", "classe", né "nuovo". la benedizione di un oggetto in un pacchetto fornisce a tale oggetto un riferimento al pacchetto e quando chiama le funzioni nel pacchetto, i loro argomenti saranno compensati di 1 slot e il primo argomento ($ _ [0] o shift) sarà equivalente a "questo" di javascript. A sua volta, è possibile simulare in qualche modo il modello prototipo di JavaScript.
Sfortunatamente, per la mia comprensione, è impossibile (a mio avviso) creare "nuove classi" in fase di esecuzione, poiché è necessario che ogni "classe" abbia il proprio pacchetto, mentre in javascript non è necessario alcun pacchetto, come "nuova" parola chiave crea una hashmap anonima da utilizzare come pacchetto in fase di esecuzione a cui è possibile aggiungere nuove funzioni e rimuovere funzioni al volo.
Ci sono alcune librerie Perl che creano i propri modi per colmare questa limitazione espressiva, come Moose.
Perché la confusione? :
Per via dei pacchetti. La nostra intuizione ci dice di legare l'oggetto a una hashmap contenente il suo 'prototipo. Questo ci consente di creare "pacchetti" in fase di esecuzione come JavaScript può. Perl non ha una tale flessibilità (almeno non integrata, è necessario inventarla o ottenerla da altri moduli) e, a sua volta, l'espressività del runtime viene ostacolata. Chiamarlo "benedica" non fa neanche molto favore.
Cosa vogliamo fare :
Qualcosa del genere, ma è vincolante per la mappa del prototipo ricorsivo ed è implicitamente legato al prototipo piuttosto che doverlo fare esplicitamente.
Ecco un tentativo ingenuo: il problema è che "call" non sa "come lo ha chiamato", quindi potrebbe anche essere una funzione perl universale "objectInvokeMethod (oggetto, metodo)" che controlla se l'oggetto ha il metodo , o il suo prototipo ce l'ha, o il suo prototipo ce l'ha, fino a quando non raggiunge la fine e la trova o meno (eredità prototipica). Perl ha una bella magia eval da fare, ma lo lascerò per qualcosa che posso provare a fare in seguito.
Comunque ecco l'idea:
(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'));
})->();
Speriamo comunque che qualcuno troverà utile questo post.
my $o = bless {}, $anything;
benedirà un oggetto nella $anything
classe. Allo stesso modo, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
creerà un metodo chiamato 'somesub' nella classe chiamata in $anything
. Tutto ciò è possibile in fase di esecuzione. "Possibile", tuttavia, non lo rende una buona pratica esercitare nel codice di tutti i giorni. Ma è utile nella costruzione di sistemi di sovrapposizione di oggetti come Moose o Moo.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
richiesta. Immagino che la mia preoccupazione alla fine si sia ridotta a essere significativamente meno intuitivo per manipolare / introspettare il sistema di pacchetti in fase di esecuzione, ma finora non ho mostrato nulla che intrinsecamente non potesse fare. Il sistema di pacchetti sembra supportare tutti gli strumenti necessari per aggiungere / rimuovere / ispezionare / modificare se stesso in fase di esecuzione.
Insieme a una serie di buone risposte, ciò che distingue in modo specifico un bless
riferimento -ed è che SV
per esso raccoglie un ulteriore FLAGS
( OBJECT
) e unSTASH
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'
Stampa, con le stesse (e irrilevanti per questo) parti soppresse
SV = IV (0x12d5530) a 0x12d5540 REFCNT = 1 BANDIERE = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) a 0x12a5a68 REFCNT = 1 FLAGS = (SHAREKEYS) ... SV = IV (0x12a5ce0) a 0x12a5cf0 REFCNT = 1 BANDIERE = (IOK, pIOK) IV = 1 --- SV = IV (0x12cb8b8) a 0x12cb8c8 REFCNT = 1 BANDIERE = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) a 0x12c26b0 REFCNT = 1 FLAGS = (OGGETTO, SHAREKEYS) STASH = 0x12d5300 "Classe" ... SV = IV (0x12c26b8) a 0x12c26c8 REFCNT = 1 BANDIERE = (IOK, pIOK) IV = 10
Con ciò si sa che 1) è un oggetto 2) a quale pacchetto appartiene, e questo ne informa il suo utilizzo.
Ad esempio, quando viene rilevata la dereferenziazione su quella variabile ( $obj->name
), viene cercato un sottotitolo con quel nome nel pacchetto (o gerarchia), l'oggetto viene passato come primo argomento, ecc.
Seguendo questo pensiero per guidare lo sviluppo Perl orientato agli oggetti.
Bless associa qualsiasi riferimento alla struttura dei dati a una classe. Dato come Perl crea la struttura di ereditarietà (in una specie di albero) è facile sfruttare il modello a oggetti per creare oggetti per la composizione.
Per questa associazione che abbiamo chiamato oggetto, per sviluppare bisogna sempre tenere presente che lo stato interno dell'oggetto e i comportamenti di classe sono separati. E puoi benedire / consentire a qualsiasi riferimento ai dati di utilizzare qualsiasi comportamento di pacchetto / classe. Dal momento che il pacchetto può comprendere lo stato "emotivo" dell'oggetto.
Ad esempio, se puoi essere sicuro che qualsiasi oggetto Bug sarà un hash benedetto, puoi (finalmente!) Compilare il codice mancante nel metodo Bug :: print_me:
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";
}
Ora, ogni volta che il metodo print_me viene chiamato tramite un riferimento a qualsiasi hash che è stato benedetto nella classe Bug, la variabile $ self estrae il riferimento che è stato passato come primo argomento e quindi le istruzioni print accedono alle varie voci dell'hash benedetto.