Bir sınıfın "yeni" yöntemi içinde Perl "bless" anahtar kelimesini kullandığını anlıyorum:
sub new {
my $self = bless { };
return $self;
}
Peki "kutsama" bu karma referans için tam olarak ne yapıyor?
Bir sınıfın "yeni" yöntemi içinde Perl "bless" anahtar kelimesini kullandığını anlıyorum:
sub new {
my $self = bless { };
return $self;
}
Peki "kutsama" bu karma referans için tam olarak ne yapıyor?
Yanıtlar:
Genel olarak, bless
bir nesneyi bir sınıfla ilişkilendirir.
package MyClass;
my $object = { };
bless $object, "MyClass";
Şimdi bir yöntemi çağırdığınızda $object
, Perl yöntemi hangi paketi arayacağını biliyor.
Örneğinizde olduğu gibi ikinci argüman atlanırsa, geçerli paket / sınıf kullanılır.
Anlaşılır olması için, örneğin aşağıdaki gibi yazılabilir:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
DÜZENLEME: Biraz daha ayrıntı için kixx'in iyi cevabına bakınız .
bless
referansı bir paketle ilişkilendirir.
Referansın ne olduğu önemli değil, bir karma (en yaygın durum), bir dizi (çok yaygın değil), bir skalere (genellikle bu bir iç-dış nesneyi gösterir ), normal bir ifadeye olabilir , altyordam veya TYPEGLOB ( Nesne Odaklı Perl: Damian Conway'in Kavramlar ve Programlama Teknikleri için Kapsamlı Bir Kılavuz adlı kitabına bakın ) ve hatta bir dosya veya dizin tanıtıcısına başvuru (en az yaygın durum).
Etkisi bless
, kutsanmış referansa özel sözdizimi uygulamanıza izin vermesidir.
Örneğin, kutsanmış bir referans depolanırsa $obj
( bless
"Class" paketi ile ilişkilendirilir ), $obj->foo(@args)
bir alt rutini çağırır foo
ve ilk argüman olarak referansı ve $obj
ardından argümanların ( @args
) geri kalanını iletir . Altyordam "Sınıf" paketinde tanımlanmalıdır. foo
"Class" paketinde alt rutin yoksa, diğer paketlerin bir listesi ( @ISA
"Class" paketindeki diziden alınır ) aranır ve bulunan ilk alt rutin foo
çağrılır.
Bu işlev, REF tarafından başvurulan varlığa artık CLASSNAME paketindeki bir nesne veya CLASSNAME belirtilmezse geçerli paket olduğunu bildirir. Kutsama iki argüman biçiminin kullanılması tavsiye edilir.
Örnek :
bless REF, CLASSNAME
bless REF
Geri dönüş değeri
Bu işlev, CLASSNAME ile kutsanmış bir nesneye başvuruyu döndürür.
Örnek :
Temel kullanımını gösteren örnek kod aşağıdadır, nesne referansı, paketin sınıfına bir referans kutsamakla oluşturulur.
#!/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;
}
Buraya bir cevap vereceğim, çünkü buradakiler benim için tıklamamıştı.
Perl'in kutsama işlevi, herhangi bir referansı bir paket içindeki tüm işlevlerle ilişkilendirir.
Neden buna ihtiyacımız var?
JavaScript'te bir örnek belirterek başlayalım:
(() => {
'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
})();
Şimdi sınıf yapısını kaldırıp onsuz yapalım:
(() => {
'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);
})();
İşlev, sıralanmamış özelliklerin bir karma tablosunu alır (2016'da dinamik dillerde belirli bir sırayla özellik yazmak zorunda kalmaz) ve bu özelliklere sahip bir karma tablo döndürür veya yeni anahtar kelimeyi koymayı unuttuysanız, tüm global bağlamı döndürür (örn. tarayıcıdaki pencere veya global olarak nodejs).
Perl'de "bu", "yeni" veya "sınıf" yoktur, ancak yine de benzer şekilde davranan bir işlevi olabilir. Bir kurucumuz ya da bir prototipimiz olmayacak, ancak istedikleri zaman yeni hayvanlar yaratabilecek ve bireysel özelliklerini değiştirebileceğiz.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Şimdi bir sorunumuz var: Ya hayvanın seslerini basmak yerine sesleri kendileri çalmasını istiyorsak. Yani, bir fonksiyonun gerçekleştirilmesini istiyoruzHayvanın kendi sesini basan ses.
Bunu yapmanın bir yolu, her bir hayvana sesin nasıl yapılacağını öğretmektir. Bu, her Cat'in gerçekleştirmesi için kendi çoğaltma işlevine sahip olduğu anlamına gelir.
# 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'}();
})->();
Bu kötüdür çünkü performSound, bir hayvan her inşa edildiğinde tamamen yeni bir fonksiyon nesnesi olarak konur. 10000 hayvan 10000 performans anlamına gelir. Kendi seslerini arayan ve basan tüm hayvanlar tarafından kullanılan tek bir işleve sahip olmak istiyoruz.
(() => {
'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
})();
İşte Perl'e paralel olarak burada durur.
JavaScript'in yeni işleci isteğe bağlı değildir, onsuz "nesne" içindeki nesne yöntemleri global kapsamı bozar:
(() => {
// '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.
})();
Her hayvan için, yapımda kodlamak yerine o hayvanın kendi sesini arayan bir fonksiyona sahip olmak istiyoruz.
Blessing, bir paketi nesnelerin prototipi olarak kullanmamızı sağlar. Bu şekilde, nesne "başvuruda bulunulan" paketin farkındadır ve paketteki işlevlerin "paket nesnesinin" yapıcısından oluşturulan belirli örneklere "ulaşmasını" sağlayabilir:
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();
Özet / TL; DR :
Perl'in "bu", "sınıf" veya "yeni" yoktur. bir nesneyi bir pakete kutsamak, o nesneye pakete bir başvuru verir ve paketteki işlevleri çağırdığında, argümanları 1 yuva ile dengelenir ve ilk argüman ($ _ [0] veya shift) javascript "bu" dır. Buna karşılık, JavaScript'in prototip modelini biraz taklit edebilirsiniz.
Ne yazık ki, her bir "sınıfın" kendi paketine sahip olması gerektiğinden, çalışma zamanında "yeni sınıflar" oluşturmayı imkansız kılarken, javascript'te "yeni" anahtar kelime olarak hiçbir pakete ihtiyacınız yoktur çalışma zamanında yeni işlevler ekleyebileceğiniz ve işlevleri anında kaldırabileceğiniz bir paket olarak kullanmanız için anonim bir hashmap oluşturur.
Moose gibi ifadede bu sınırlamayı köprülemek için kendi yollarını yaratan bazı Perl kütüphaneleri var.
Neden karışıklık? :
Paketler yüzünden. Sezgimiz bize nesneyi 'prototipini içeren bir hashmap'a bağlamamızı söylüyor. Bu, çalışma zamanında JavaScript'in yapabileceği gibi "paketler" oluşturmamızı sağlar. Perl böyle bir esnekliğe sahip değildir (en azından yerleşik değildir, onu icat etmeniz veya diğer modüllerden almanız gerekir) ve sırayla çalışma zamanı ifadeniz engellenir. Ona “kutsama” deme, pek de iyilik yapmaz.
Ne yapmak istiyoruz :
Bunun gibi bir şey, ancak prototip haritasına özyinelemeli bağlanma var ve bunu açıkça yapmak yerine prototipe dolaylı olarak bağlı olun.
İşte naif bir girişim: sorun "çağrı" bilmiyor "ne denir", bu yüzden de nesnenin yöntemi olup olmadığını kontrol evrensel bir perl işlevi "objectInvokeMethod (nesne, yöntem)" olabilir veya prototipine sahip olur ya da prototipine kadar, sonuna ulaşıp bulana kadar (prototipik miras). Perl'in bunu yapmak için güzel bir değerlendirme büyüsü var ama bunu daha sonra deneyebileceğim bir şey için bırakacağım.
Her neyse, burada fikir:
(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'));
})->();
Neyse umarım birisi bu yazıyı faydalı bulacaktır.
my $o = bless {}, $anything;
bir nesneyi kutsasın $anything
. Benzer şekilde, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
adlı sınıfta 'somesub' adlı bir yöntem oluşturur $anything
. Bunların hepsi çalışma zamanında mümkündür. Ancak "Mümkün", günlük kodda kullanılmasını pek iyi bir uygulama haline getirmiyor. Ancak Moose veya Moo gibi nesne bindirme sistemlerinin oluşturulmasında yararlıdır.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
kılıyor. Endişem sonunda sonuçta paket sistemini manipüle etmek / introspect etmek için önemli ölçüde daha az sezgisel olmakla kaygılıydı, ancak şimdiye kadar doğal olarak yapamayacağı bir şey gösteremedim. Paket sistemi, çalışma zamanında kendini eklemek / kaldırmak / incelemek / değiştirmek için gerekli tüm araçları destekliyor gibi görünüyor.
Bir dizi iyi cevabın yanı sıra, a bless
-ed referansını özel olarak ayıran şey, bunun SV
için bir ek FLAGS
( OBJECT
) ve birSTASH
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'
Aynı (ve bununla alakasız) parçaların bastırılmasıyla yazdırılır
SV = IV (0x12d5530), 0x12d5540'ta REFCNT = 1 BAYRAK = (ROK) RV = 0x12a5a68 SV = 0x12a5a68'de PVHV (0x12ab980) REFCNT = 1 BAYRAK = (SHAREKEYS) ... SV = IV (0x12a5ce0), 0x12a5cf0 konumunda REFCNT = 1 BAYRAK = (IOK, pIOK) IV = 1 --- SV = IV (0x12cb8b8), 0x12cb8c8'de REFCNT = 1 BAYRAK = (PADMY, ROK) RV = 0x12c26b0 SV = 0x12c26b0'da PVHV (0x12aba00) REFCNT = 1 BAYRAK = (NESNE, SHAREKEYS) STASH = 0x12d5300 "Sınıf" ... SV = IV (0x12c26b8), 0x12c26c8'de REFCNT = 1 BAYRAK = (IOK, pIOK) IV = 10
Bununla, 1) bir nesnenin 2) hangi pakete ait olduğu bilinir ve bu onun kullanımını bilgilendirir.
Örneğin, bu değişken üzerinde kayıt silme ile karşılaşıldığında ( $obj->name
), pakette (veya hiyerarşide) bu ada sahip bir alt öğe aranır, nesne ilk argüman olarak geçirilir.
I Bu düşünceyi takiben gelişim nesnesine yönelik Perl'e rehberlik etmek.
Bless, herhangi bir veri yapısı referansını bir sınıfla ilişkilendirir. Perl'in kalıtım yapısını (bir tür ağaçta) nasıl yarattığı göz önüne alındığında, kompozisyon için Nesneler oluşturmak için nesne modelinden yararlanmak kolaydır.
Nesne olarak adlandırdığımız bu ilişki için, geliştirmek her zaman nesnenin iç durumunun ve sınıf davranışlarının ayrıldığını göz önünde bulundurur. Ve herhangi bir veri referansının herhangi bir paket / sınıf davranışını kullanmasına izin verebilir / izin verebilirsiniz. Paket nesnenin "duygusal" durumunu anlayabildiğinden.
Örneğin, herhangi bir Bug nesnesinin kutsanmış bir karma olacağından eminseniz, (::!) Eksik kodu Bug :: print_me yönteminde doldurabilirsiniz:
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";
}
Şimdi, print_me yöntemi Bug sınıfına kutsanmış herhangi bir karmaya başvuru yoluyla çağrıldığında, $ self değişkeni ilk bağımsız değişken olarak iletilen başvuruyu ayıklar ve sonra print deyimleri kutsanmış karmanın çeşitli girişlerine erişir.