Rozumiem, że słowo kluczowe „bless” w Perlu jest używane wewnątrz metody „nowej” klasy:
sub new {
my $self = bless { };
return $self;
}
Ale co dokładnie robi „błogosławieństwo” z tym hashem?
Rozumiem, że słowo kluczowe „bless” w Perlu jest używane wewnątrz metody „nowej” klasy:
sub new {
my $self = bless { };
return $self;
}
Ale co dokładnie robi „błogosławieństwo” z tym hashem?
Odpowiedzi:
Ogólnie rzecz biorąc, bless
wiąże obiekt z klasą.
package MyClass;
my $object = { };
bless $object, "MyClass";
Teraz, kiedy wywołujesz metodę $object
, Perl wie, w którym pakiecie szukać metody.
Jeśli drugi argument zostanie pominięty, jak w przykładzie, używany jest bieżący pakiet / klasa.
Ze względu na przejrzystość przykład można zapisać w następujący sposób:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
EDYCJA: Zobacz dobrą odpowiedź kixx po trochę więcej szczegółów.
bless
kojarzy odniesienie z pakietem.
Nie ma znaczenia, do czego jest odniesienie, może to być hash (najczęstszy przypadek), tablica (niezbyt często), skalar (zwykle oznacza to obiekt wywrócony na drugą stronę ), wyrażenie regularne , subroutine lub TYPEGLOB (zobacz książkę Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques autorstwa Damiana Conwaya, gdzie znajdziesz przydatne przykłady) lub nawet odniesienie do uchwytu pliku lub katalogu (najmniej powszechny przypadek).
Efektem bless
jest to, że pozwala ci zastosować specjalną składnię do błogosławionego odniesienia.
Na przykład, jeśli błogosławione odniesienie jest przechowywane w $obj
(skojarzone bless
z pakietem "Klasa"), to $obj->foo(@args)
wywoła podprogram foo
i przekaże jako pierwszy argument odwołanie, $obj
po którym nastąpi reszta argumentów ( @args
). Podprogram należy zdefiniować w pakiecie „Class”. Jeśli nie ma podprogramu foo
w pakiecie „Class”, @ISA
przeszukana zostanie lista innych pakietów (wziętych z tablicy w pakiecie „Class”) i foo
zostanie wywołany pierwszy znaleziony podprogram .
Ta funkcja informuje jednostkę, do której odwołuje się REF, że jest teraz obiektem w pakiecie CLASSNAME lub bieżącym pakietem, jeśli pominięto CLASSNAME. Zalecane jest użycie dwuargumentowej formy błogosławieństwa.
Przykład :
bless REF, CLASSNAME
bless REF
Wartość zwracana
Ta funkcja zwraca odniesienie do obiektu przekazanego do CLASSNAME.
Przykład :
Poniżej znajduje się przykładowy kod pokazujący jego podstawowe użycie, odwołanie do obiektu jest tworzone przez błogosławieństwo odniesienia do klasy pakietu -
#!/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;
}
Udzielę odpowiedzi tutaj, ponieważ te tutaj nie kliknęły dla mnie.
Funkcja błogosławieństwa Perla kojarzy wszelkie odniesienia do wszystkich funkcji wewnątrz pakietu.
Po co nam to?
Zacznijmy od przedstawienia przykładu w 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
})();
Teraz usuńmy konstrukcję klasy i obejdźmy się bez niej:
(() => {
'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);
})();
Funkcja pobiera tablicę skrótów z nieuporządkowanymi właściwościami (ponieważ nie ma sensu pisać właściwości w określonej kolejności w językach dynamicznych w 2016 r.) I zwraca tabelę skrótów z tymi właściwościami lub jeśli zapomniałeś wstawić nowe słowo kluczowe, zwróci cały kontekst globalny (np. okno w przeglądarce lub globalny w nodejs).
Perl nie ma „tego”, „nowego” ani „klasy”, ale nadal może mieć funkcję, która zachowuje się podobnie. Nie będziemy mieli konstruktora ani prototypu, ale będziemy mogli dowolnie tworzyć nowe zwierzęta i modyfikować ich indywidualne właściwości.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Teraz mamy problem: co by było, gdybyśmy chcieli, aby zwierzę samodzielnie odtwarzało dźwięki, zamiast drukować, jaki jest ich głos. Oznacza to, że chcemy, aby funkcja performSound wyświetlała dźwięk własnego zwierzęcia.
Jednym ze sposobów jest nauczenie każdego zwierzęcia, jak ma robić to, co słychać. Oznacza to, że każdy kot ma swoją zduplikowaną funkcję do wykonywania dźwięku.
# 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'}();
})->();
Jest to złe, ponieważ performSound jest umieszczany jako zupełnie nowy obiekt funkcji za każdym razem, gdy konstruowane jest zwierzę. 10000 zwierząt to 10000 wykonanych dźwięków. Chcemy, aby jedna funkcja performSound była używana przez wszystkie zwierzęta, które wyszukują własne dźwięki i je drukują.
(() => {
'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
})();
Tutaj kończy się paralela do Perla.
Nowy operator JavaScript nie jest opcjonalny, bez niego „this” wewnątrz metod obiektu psuje globalny zasięg:
(() => {
// '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.
})();
Chcemy mieć jedną funkcję dla każdego zwierzęcia, która wyszukuje własny dźwięk tego zwierzęcia, zamiast zakodować go na sztywno w konstrukcji.
Błogosławieństwo pozwala nam używać pakietu jako prototypu obiektów. W ten sposób obiekt jest świadomy „pakietu”, do którego „się odwołuje”, i z kolei może mieć funkcje w pakiecie „sięgać” do określonych instancji, które zostały utworzone na podstawie konstruktora tego „obiektu pakietu”:
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();
Podsumowanie / TL; DR :
Perl nie ma „tego”, „klasy” ani „nowego”. pobłogosławienie obiektu pakietowi daje temu obiektowi odniesienie do pakietu, a kiedy wywołuje funkcje w pakiecie, ich argumenty zostaną przesunięte o 1 szczelinę, a pierwszy argument ($ _ [0] lub shift) będzie równoważny javascript's „this”. Możesz z kolei nieco symulować prototypowy model JavaScript.
Niestety uniemożliwia to (w moim rozumieniu) tworzenie "nowych klas" w czasie wykonywania, ponieważ każda "klasa" musi mieć swój własny pakiet, podczas gdy w javascript w ogóle nie potrzebujesz pakietów, jako słowo kluczowe "nowe" tworzy anonimowy skrót, którego możesz używać jako pakietu w czasie wykonywania, do którego możesz dodawać nowe funkcje i usuwać funkcje w locie.
Istnieją biblioteki Perla, które tworzą własne sposoby pokonywania tego ograniczenia ekspresji, na przykład Moose.
Dlaczego zamieszanie? :
Z powodu pakietów. Nasza intuicja podpowiada nam, aby związać obiekt z hasmapą zawierającą jego prototyp. To pozwala nam tworzyć „pakiety” w czasie wykonywania, jak JavaScript. Perl nie ma takiej elastyczności (przynajmniej nie jest wbudowany, musisz go wymyślić lub pobrać z innych modułów), co z kolei utrudnia ekspresję w środowisku uruchomieniowym. Nazywanie tego „błogosławieństwem” również nie przynosi zbyt wielu korzyści.
Co chcemy zrobić :
Coś podobnego do tego, ale mają powiązanie z mapą prototypu rekurencyjnie i są niejawnie powiązane z prototypem, zamiast robić to jawnie.
Oto naiwna próba: problem polega na tym, że „call” nie wie „co go wywołało”, więc równie dobrze może to być uniwersalna funkcja perla „objectInvokeMethod (obiekt, metoda)”, która sprawdza, czy obiekt ma metodę , albo ma go jego prototyp, albo ma go jego prototyp, dopóki nie dotrze do końca i go nie znajdzie (dziedziczenie prototypowe). Perl ma do tego niezłą magię ewaluacyjną, ale zostawię to na coś, co będę mógł zrobić później.
Tak czy inaczej, oto pomysł:
(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'));
})->();
Miejmy nadzieję, że ktoś uzna ten post za przydatny.
my $o = bless {}, $anything;
pobłogosławi obiekt w $anything
klasie. Podobnie {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
utworzy metodę o nazwie „somesub” w klasie o nazwie w $anything
. To wszystko jest możliwe w czasie wykonywania. Jednak „możliwe” nie oznacza, że posługiwanie się w codziennym kodzie jest świetną praktyką. Ale jest przydatny w budowaniu systemów nakładania obiektów, takich jak Moose lub Moo.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
roszczenie. Wydaje mi się, że moje obawy ostatecznie sprowadziły się do tego, że manipulowanie / introspekcja systemu pakietów w czasie wykonywania jest znacznie mniej intuicyjne, ale jak dotąd nie udało mi się pokazać niczego, czego z natury nie może zrobić. Wydaje się, że system pakietów obsługuje wszystkie narzędzia potrzebne do dodawania / usuwania / sprawdzania / modyfikowania samego siebie w czasie wykonywania.
Oprócz wielu dobrych odpowiedzi, tym, co szczególnie wyróżnia bless
odwołanie z a -ed, jest to, że SV
dla niego pobiera dodatkowe FLAGS
( OBJECT
) iSTASH
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'
Wydruki z wyłączonymi tymi samymi (i nieistotnymi do tego) częściami
SV = IV (0x12d5530) pod adresem 0x12d5540 REFCNT = 1 FLAGI = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) pod adresem 0x12a5a68 REFCNT = 1 FLAGI = (SHAREKEYS) ... SV = IV (0x12a5ce0) przy 0x12a5cf0 REFCNT = 1 FLAGI = (IOK, pIOK) IV = 1 --- SV = IV (0x12cb8b8) przy 0x12cb8c8 REFCNT = 1 FLAGI = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) pod adresem 0x12c26b0 REFCNT = 1 FLAGI = (OBIEKT, SHAREKEYS) STASH = 0x12d5300 "Klasa" ... SV = IV (0x12c26b8) pod adresem 0x12c26c8 REFCNT = 1 FLAGI = (IOK, pIOK) IV = 10
Dzięki temu wiadomo, że 1) jest obiektem, 2) do jakiego pakietu należy, a to informuje o jego użyciu.
Na przykład, gdy napotkano wyłuskiwanie tej zmiennej ( $obj->name
), podrzędna nazwa o tej nazwie jest poszukiwana w pakiecie (lub hierarchii), obiekt jest przekazywany jako pierwszy argument itp.
I Zgodnie z tą myślą, aby poprowadzić programowanie zorientowanego obiektowo Perla.
Błogosławieństwo skojarz dowolne odniesienie do struktury danych z klasą. Biorąc pod uwagę, w jaki sposób Perl tworzy strukturę dziedziczenia (w rodzaju drzewa), łatwo jest wykorzystać model obiektowy do tworzenia obiektów do kompozycji.
Do tego skojarzenia nazwaliśmy obiekt, aby rozwijać zawsze mając na uwadze, że stan wewnętrzny obiektu i zachowania klasowe są rozdzielone. Możesz też pobłogosławić / zezwolić dowolnemu odwołaniu do danych na użycie dowolnego zachowania pakietu / klasy. Ponieważ opakowanie może zrozumieć „emocjonalny” stan obiektu.
Na przykład, jeśli możesz być pewien, że jakikolwiek obiekt Bug będzie błogosławionym hashem, możesz (w końcu!) Uzupełnić brakujący kod w metodzie 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";
}
Teraz, ilekroć metoda print_me jest wywoływana przez odniesienie do dowolnego skrótu, który został pobłogosławiony w klasie Bug, zmienna $ self wyodrębnia referencję, która została przekazana jako pierwszy argument, a następnie instrukcje print uzyskują dostęp do różnych wpisów błogosławionego skrótu.