Saya mengerti seseorang menggunakan kata kunci "berkah" di Perl di dalam metode "baru" kelas:
sub new {
my $self = bless { };
return $self;
}
Tapi apa sebenarnya yang "diberkati" lakukan untuk referensi hash?
Saya mengerti seseorang menggunakan kata kunci "berkah" di Perl di dalam metode "baru" kelas:
sub new {
my $self = bless { };
return $self;
}
Tapi apa sebenarnya yang "diberkati" lakukan untuk referensi hash?
Jawaban:
Secara umum, bless
mengaitkan objek dengan kelas.
package MyClass;
my $object = { };
bless $object, "MyClass";
Sekarang ketika Anda memanggil metode aktif $object
, Perl tahu paket mana yang harus dicari.
Jika argumen kedua dihilangkan, seperti pada contoh Anda, paket / kelas saat ini digunakan.
Demi kejelasan, contoh Anda dapat ditulis sebagai berikut:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
bless
menghubungkan referensi dengan suatu paket.
Tidak peduli apa rujukannya, bisa berupa hash (kasus paling umum), ke array (tidak begitu umum), ke skalar (biasanya ini menunjukkan objek luar-dalam ), ke ekspresi reguler , subroutine atau TYPEGLOB (lihat buku Object Oriented Perl: Sebuah Panduan Komprehensif untuk Konsep dan Teknik Pemrograman oleh Damian Conway untuk contoh yang berguna) atau bahkan referensi ke file atau direktori handle (kasus paling umum).
Efek yang bless
dimiliki adalah memungkinkan Anda untuk menerapkan sintaks khusus untuk referensi yang diberkati.
Misalnya, jika referensi yang diberkati disimpan di $obj
(terkait bless
dengan paket "Kelas"), maka $obj->foo(@args)
akan memanggil subrutin foo
dan lulus sebagai argumen pertama referensi $obj
diikuti oleh sisa argumen ( @args
). Subrutin harus didefinisikan dalam paket "Kelas". Jika tidak ada subrutin foo
dalam paket "Kelas", daftar paket lain (diambil dari larik @ISA
dalam paket "Kelas") akan dicari dan subrutin pertama yang foo
ditemukan akan dipanggil.
Fungsi ini memberi tahu entitas yang dirujuk oleh REF bahwa itu sekarang menjadi objek dalam paket CLASSNAME, atau paket saat ini jika CLASSNAME dihilangkan. Dianjurkan untuk menggunakan dua argumen bentuk berkat.
Contoh :
bless REF, CLASSNAME
bless REF
Nilai Pengembalian
Fungsi ini mengembalikan referensi ke objek yang diberkati menjadi CLASSNAME.
Contoh :
Berikut ini adalah contoh kode yang menunjukkan penggunaan dasarnya, referensi objek dibuat dengan memberkati referensi ke kelas paket -
#!/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;
}
Saya akan memberikan jawaban di sini karena yang di sini tidak cukup klik untuk saya.
Fungsi memberkati Perl menghubungkan referensi apa pun ke semua fungsi di dalam sebuah paket.
Mengapa kita membutuhkan ini?
Mari kita mulai dengan mengungkapkan contoh dalam 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
})();
Sekarang mari kita hapus konstruk kelas dan lakukan tanpa itu:
(() => {
'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);
})();
Fungsi ini mengambil tabel hash dari properti tidak berurutan (karena tidak masuk akal harus menulis properti dalam urutan tertentu dalam bahasa dinamis di 2016) dan mengembalikan tabel hash dengan properti itu, atau jika Anda lupa memasukkan kata kunci baru, itu akan mengembalikan seluruh konteks global (mis. jendela di browser atau global dalam nodejs).
Perl tidak memiliki "ini" atau "baru" atau "kelas", tetapi masih dapat memiliki fungsi yang berperilaku sama. Kami tidak akan memiliki konstruktor atau prototipe, tetapi kami akan dapat membuat hewan baru sesuka hati dan memodifikasi properti masing-masing.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Sekarang, kita memiliki masalah: Bagaimana jika kita ingin hewan itu melakukan suara sendiri alih-alih kita mencetak suara mereka. Artinya, kami ingin fungsi performSound yang mencetak suara binatang itu sendiri.
Salah satu cara untuk melakukan ini adalah dengan mengajar masing-masing Hewan bagaimana melakukannya dengan suara. Ini berarti bahwa masing-masing Cat memiliki fungsi duplikat sendiri untuk melakukan Suara.
# 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'}();
})->();
Ini buruk karena performSound diletakkan sebagai objek fungsi yang sama sekali baru setiap kali seekor hewan dikonstruksi. 10.000 hewan berarti 10.000 performSounds. Kami ingin memiliki satu fungsi performSound yang digunakan oleh semua hewan yang mencari suara mereka sendiri dan mencetaknya.
(() => {
'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
})();
Di sinilah paralel ke Perl agak berhenti.
Operator baru JavaScript tidak opsional, tanpa itu, "ini" di dalam metode objek merusak lingkup global:
(() => {
// '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.
})();
Kami ingin memiliki satu fungsi untuk masing-masing Hewan yang mencari suara binatang itu sendiri daripada mengkodekannya saat konstruksi.
Berkat memungkinkan kita menggunakan paket sebagai prototipe objek. Dengan cara ini, objek menyadari "paket" itu "dirujuk", dan pada gilirannya dapat memiliki fungsi dalam paket "menjangkau" contoh spesifik yang dibuat dari konstruktor "objek paket" itu:
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();
Ringkasan / TL; DR :
Perl tidak memiliki "ini", "kelas", atau "baru". memberkati objek ke suatu paket memberikan objek itu referensi ke paket, dan ketika ia memanggil fungsi dalam paket, argumen mereka akan diimbangi dengan 1 slot, dan argumen pertama ($ _ [0] atau shift) akan setara dengan javascript "ini". Pada gilirannya, Anda dapat mensimulasikan model prototipe JavaScript.
Sayangnya itu tidak memungkinkan (untuk pengertian saya) untuk membuat "kelas baru" saat runtime, karena Anda membutuhkan setiap "kelas" untuk memiliki paket sendiri, sedangkan di javascript, Anda tidak perlu paket sama sekali, sebagai kata kunci "baru" membuat hashmap anonim untuk Anda gunakan sebagai paket saat runtime yang Anda dapat menambahkan fungsi baru dan menghapus fungsi dengan cepat.
Ada beberapa perpustakaan Perl menciptakan cara mereka sendiri untuk menjembatani keterbatasan ini dalam ekspresi, seperti Moose.
Kenapa kebingungan? :
Karena paket. Intuisi kita memberitahu kita untuk mengikat objek ke sebuah hashmap yang berisi prototipe. Ini memungkinkan kami membuat "paket" pada saat runtime seperti yang dilakukan JavaScript. Perl tidak memiliki fleksibilitas seperti itu (setidaknya tidak built-in, Anda harus menciptakannya atau mendapatkannya dari modul lain), dan pada gilirannya runtime ekspresivitas Anda terhambat. Menyebutnya "berkah" juga tidak banyak membantu.
Apa yang ingin kita lakukan :
Sesuatu seperti ini, tetapi memiliki ikatan ke prototipe peta rekursif, dan secara implisit terikat pada prototipe daripada harus secara eksplisit melakukannya.
Berikut ini adalah upaya naif untuk itu: masalahnya adalah bahwa "panggilan" tidak tahu "apa yang disebutnya", sehingga mungkin juga menjadi fungsi perl universal "objectInvokeMethod (objek, metode)" yang memeriksa apakah objek memiliki metode , atau prototipe memilikinya, atau prototipe memilikinya, hingga mencapai akhir dan menemukannya atau tidak (pewarisan prototipe). Perl memiliki sihir eval yang bagus untuk melakukannya, tetapi saya akan membiarkannya untuk sesuatu yang bisa saya coba nanti.
Bagaimanapun, inilah idenya:
(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'));
})->();
Pokoknya mudah-mudahan seseorang akan menemukan posting ini bermanfaat.
my $o = bless {}, $anything;
akan memberkati objek ke dalam $anything
kelas. Demikian pula, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
akan membuat metode bernama 'somesub' di kelas yang bernama in $anything
. Ini semua dimungkinkan saat runtime. "Kemungkinan", bagaimanapun, tidak membuatnya menjadi praktik yang hebat untuk menggunakan kode setiap hari. Tetapi ini berguna dalam membangun sistem overlay objek seperti Moose atau Moo.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
klaim saya . Saya kira kekhawatiran saya akhirnya menjadi kurang intuitif untuk memanipulasi / mengintrospeksi sistem paket pada saat runtime, tetapi sejauh ini saya telah gagal menunjukkan sesuatu yang secara inheren tidak dapat dilakukan. Sistem paket tampaknya mendukung semua alat yang diperlukan untuk menambah / menghapus / memeriksa / memodifikasi sendiri saat runtime.
Seiring dengan sejumlah jawaban yang baik, apa yang secara khusus membedakan bless
referensi -ed adalah bahwa SV
untuk mengambil tambahan FLAGS
( OBJECT
) danSTASH
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'
Cetakan, dengan bagian yang sama (dan tidak relevan untuk ini) ditekan
SV = IV (0x12d5530) pada 0x12d5540 REFCNT = 1 BENDERA = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) pada 0x12a5a68 REFCNT = 1 BENDERA = (SHAREKEYS) ... SV = IV (0x12a5ce0) pada 0x12a5cf0 REFCNT = 1 BENDERA = (IOK, pIOK) IV = 1 --- SV = IV (0x12cb8b8) pada 0x12cb8c8 REFCNT = 1 BENDERA = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) pada 0x12c26b0 REFCNT = 1 BENDERA = (OBYEK, SHAREKEYS) STASH = 0x12d5300 "Kelas" ... SV = IV (0x12c26b8) pada 0x12c26c8 REFCNT = 1 BENDERA = (IOK, pIOK) IV = 10
Dengan itu diketahui bahwa 1) itu adalah objek 2) paket miliknya, dan ini menginformasikan penggunaannya.
Sebagai contoh, ketika dereferencing pada variabel itu ditemui ( $obj->name
), sub dengan nama itu dicari dalam paket (atau hierarki), objek dilewatkan sebagai argumen pertama, dll.
Saya mengikuti pemikiran ini untuk memandu pengembangan berorientasi objek Perl.
Bless kaitkan referensi struktur data apa pun dengan kelas. Mengingat bagaimana Perl menciptakan struktur warisan (dalam semacam pohon), mudah untuk mengambil keuntungan dari model objek untuk membuat Objek untuk komposisi.
Untuk asosiasi ini kita sebut objek, untuk mengembangkan selalu ada dalam pikiran bahwa keadaan internal objek dan perilaku kelas dipisahkan. Dan Anda dapat memberkati / mengizinkan referensi data untuk menggunakan perilaku paket / kelas apa pun. Karena paket dapat memahami keadaan "emosional" objek.
Misalnya, jika Anda dapat yakin bahwa objek Bug apa pun akan menjadi hash yang diberkati, Anda dapat (akhirnya!) Mengisi kode yang hilang dalam metode 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";
}
Sekarang, setiap kali metode print_me dipanggil melalui referensi ke hash apa pun yang telah diberkati ke dalam kelas Bug, variabel $ self mengekstrak referensi yang diteruskan sebagai argumen pertama dan kemudian pernyataan cetak mengakses berbagai entri hash yang diberkati.