ฉันเข้าใจหนึ่งใช้คำหลัก "อวยพร" ใน Perl ภายในวิธีการ "ใหม่" ของชั้นเรียน:
sub new {
my $self = bless { };
return $self;
}
แต่อะไรคือ "อวยพร" ที่ทำกับการอ้างอิงแฮช
ฉันเข้าใจหนึ่งใช้คำหลัก "อวยพร" ใน Perl ภายในวิธีการ "ใหม่" ของชั้นเรียน:
sub new {
my $self = bless { };
return $self;
}
แต่อะไรคือ "อวยพร" ที่ทำกับการอ้างอิงแฮช
คำตอบ:
โดยทั่วไปbless
เชื่อมโยงวัตถุกับคลาส
package MyClass;
my $object = { };
bless $object, "MyClass";
ตอนนี้เมื่อคุณเรียกใช้เมธอดบน$object
Perl รู้ว่าแพ็กเกจใดที่จะค้นหาเมธอด
หากไม่ได้ระบุอาร์กิวเมนต์ที่สองดังตัวอย่างของคุณจะใช้แพ็กเกจ / คลาสปัจจุบัน
เพื่อความชัดเจนตัวอย่างของคุณอาจถูกเขียนดังนี้:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
แก้ไข: ดูคำตอบที่ดีของkixxเพื่อดูรายละเอียดเพิ่มเติมเล็กน้อย
bless
เชื่อมโยงการอ้างอิงกับแพ็คเกจ
มันไม่สำคัญว่าการอ้างอิงนั้นจะเกี่ยวกับแฮช (กรณีที่พบบ่อยที่สุด) กับอาเรย์ (ไม่ใช่เรื่องธรรมดา) ไปยังสเกลาร์ (โดยปกติจะระบุว่าวัตถุที่อยู่ด้านใน ) ไปยังนิพจน์ทั่วไป , รูทีนย่อยหรือ TYPEGLOB (ดูหนังสือObject Oriented Perl: คู่มือครอบคลุมแนวคิดและเทคนิคการเขียนโปรแกรมโดย Damian Conwayสำหรับตัวอย่างที่มีประโยชน์) หรือแม้แต่การอ้างอิงไปยังไฟล์หรือไดเรกทอรีจัดการ (กรณีที่พบบ่อยที่สุด)
เอฟเฟกต์bless
มีคือช่วยให้คุณสามารถใช้ไวยากรณ์พิเศษกับการอ้างอิงที่ได้รับพร
ตัวอย่างเช่นหากการอ้างอิงที่มีความสุขถูกเก็บไว้ใน$obj
(เชื่อมโยงbless
กับแพ็คเกจ "คลาส") จากนั้น$obj->foo(@args)
จะเรียกรูทีนย่อยfoo
และส่งผ่านเป็นอาร์กิวเมนต์แรกการอ้างอิง$obj
ตามด้วยอาร์กิวเมนต์ที่เหลือ ( @args
) ควรกำหนดรูทีนย่อยในแพ็คเกจ "Class" หากไม่มีรูทีนย่อยfoo
ในแพ็คเกจ "Class" จะมีการค้นหารายการแพ็กเกจอื่น (นำมาจากอาร์เรย์@ISA
ในแพ็คเกจ "Class") และค้นหารูทีนย่อยแรกที่foo
พบ
เวอร์ชั่นย่อ: มันทำเครื่องหมายว่าแฮชที่แนบมากับเนมสเปซแพ็คเกจปัจจุบัน (เพื่อให้แพคเกจนั้นมีการใช้คลาส)
ฟังก์ชันนี้บอกเอนทิตีที่อ้างอิงโดย REF ว่าเป็นวัตถุในแพ็คเกจ CLASSNAME หรือแพ็คเกจปัจจุบันหากไม่ระบุ CLASSNAME ขอแนะนำให้ใช้รูปแบบพรสองข้อโต้แย้ง
ตัวอย่าง :
bless REF, CLASSNAME
bless REF
ส่งคืนค่า
ฟังก์ชันนี้ส่งคืนการอ้างอิงไปยังวัตถุที่มีความสุขใน CLASSNAME
ตัวอย่าง :
ต่อไปนี้เป็นตัวอย่างรหัสแสดงการใช้งานขั้นพื้นฐานของการอ้างอิงวัตถุที่ถูกสร้างขึ้นโดยอวยพรอ้างอิงถึงชั้นเรียนของแพคเกจ -
#!/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;
}
ฉันจะให้คำตอบที่นี่เพราะคนที่นี่ไม่ได้คลิกสำหรับฉัน
ฟังก์ชันอวยพรของ Perl เชื่อมโยงการอ้างอิงถึงทุกฟังก์ชั่นภายในแพ็คเกจ
ทำไมเราต้องการสิ่งนี้
เริ่มต้นด้วยการแสดงตัวอย่างใน 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
})();
ตอนนี้ให้ตัดการสร้างคลาสออกและทำโดยไม่ทำ:
(() => {
'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);
})();
ฟังก์ชันใช้ตารางแฮชของคุณสมบัติที่ไม่ได้เรียงลำดับ (เนื่องจากไม่มีเหตุผลที่จะต้องเขียนคุณสมบัติตามลำดับในภาษาไดนามิกในปี 2016) และส่งคืนตารางแฮชที่มีคุณสมบัติเหล่านั้นหรือหากคุณลืมใส่คำหลักใหม่ จะกลับบริบททั้งหมดทั่วโลก (เช่นหน้าต่างในเบราว์เซอร์หรือทั่วโลกใน nodejs)
Perl ไม่มี "นี่" หรือ "ใหม่" หรือ "คลาส" แต่ก็ยังสามารถมีฟังก์ชั่นที่ทำงานในทำนองเดียวกัน เราจะไม่มีคอนสตรัคเตอร์หรือต้นแบบ แต่เราจะสามารถสร้างสัตว์ใหม่ตามความประสงค์และปรับเปลี่ยนคุณสมบัติของแต่ละบุคคล
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
ตอนนี้เรามีปัญหา: จะเป็นอย่างไรถ้าเราต้องการให้สัตว์แสดงเสียงด้วยตัวเองแทนที่จะพิมพ์ออกมาเป็นเสียงของพวกมัน นั่นคือเราต้องการฟังก์ชั่นการทำงานเสียงที่พิมพ์เสียงของสัตว์
วิธีหนึ่งในการทำเช่นนี้คือการสอนให้สัตว์แต่ละตัวรู้ว่าต้องทำอย่างไร ซึ่งหมายความว่าแมวแต่ละตัวมีฟังก์ชั่นที่ซ้ำกันเพื่อทำการเสียง
# 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'}();
})->();
สิ่งนี้เป็นสิ่งที่ไม่ดีนักเนื่องจาก performSound ถูกวางเป็นวัตถุฟังก์ชั่นใหม่ที่สมบูรณ์แบบทุกครั้งที่มีการสร้างสัตว์ สัตว์ 10,000 ตัวหมายถึง 10,000 รอบเสียง เราต้องการให้มีฟังก์ชั่นเดียวทำเสียงที่สัตว์ทุกตัวใช้ค้นหาเสียงของตัวเองและพิมพ์ออกมา
(() => {
'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
})();
นี่คือที่ที่ขนานกับ Perl kinda หยุด
ตัวดำเนินการใหม่ของ JavaScript ไม่ใช่ตัวเลือกหากไม่มีวิธีนี้ภายในวิธีวัตถุจะทำลายขอบเขตทั่วโลก:
(() => {
// '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.
})();
เราต้องการที่จะมีฟังก์ชั่นเดียวสำหรับสัตว์แต่ละตัวที่ค้นหาเสียงของสัตว์ตัวนั้นมากกว่าการเข้ารหัสแบบแข็งที่การสร้าง
พรช่วยให้เราใช้แพคเกจเป็นต้นแบบของวัตถุ ด้วยวิธีนี้วัตถุจะรับรู้ถึง "แพคเกจ" มันคือ "อ้างอิงถึง" และในทางกลับกันอาจมีฟังก์ชั่นในแพคเกจ "เข้าถึงเป็น" อินสแตนซ์เฉพาะที่ถูกสร้างขึ้นจากตัวสร้างของ "แพคเกจวัตถุ":
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();
สรุป / TL; DR :
Perl ไม่มี "นี่", "คลาส" หรือ "ใหม่" การให้พรวัตถุไปยังแพ็คเกจให้วัตถุนั้นอ้างอิงถึงแพ็คเกจและเมื่อมันเรียกใช้ฟังก์ชันในแพ็คเกจอาร์กิวเมนต์ของพวกเขาจะถูกชดเชยด้วย 1 สล็อตและอาร์กิวเมนต์แรก ($ _ [0] หรือ shift) จะเทียบเท่ากับ javascript ของ "นี่" ในทางกลับกันคุณสามารถจำลองโมเดลต้นแบบของ JavaScript ได้บ้าง
น่าเสียดายที่มันทำให้เป็นไปไม่ได้ (สำหรับความเข้าใจของฉัน) ในการสร้าง "คลาสใหม่" ที่รันไทม์เนื่องจากคุณต้องการ "คลาส" แต่ละอันเพื่อให้มีแพ็คเกจของตัวเองในขณะที่ในจาวาสคริปต์คุณไม่จำเป็นต้องใช้แพ็กเกจเลย สร้าง hashmap ที่ไม่ระบุชื่อเพื่อให้คุณใช้เป็นแพ็คเกจในขณะใช้งานซึ่งคุณสามารถเพิ่มฟังก์ชั่นใหม่และลบฟังก์ชั่นต่างๆได้ทันที
มีห้องสมุด Perl บางตัวที่สร้างวิธีการของตัวเองในการลดข้อ จำกัด นี้ในลักษณะที่แสดงออกเช่น Moose
ทำไมต้องสับสน? :
เพราะแพคเกจ สัญชาตญาณของเราบอกให้เราผูกวัตถุกับ hashmap ที่มีต้นแบบของมัน สิ่งนี้ช่วยให้เราสร้าง "แพ็คเกจ" ที่รันไทม์อย่างที่ JavaScript สามารถทำได้ Perl ไม่ได้มีความยืดหยุ่นดังกล่าว (อย่างน้อยไม่ได้สร้างในคุณต้องประดิษฐ์มันหรือได้รับจากโมดูลอื่น ๆ ) และในทางกลับกันการแสดงออกของรันไทม์ของคุณจะถูกขัดขวาง การเรียกมันว่า "อวยพร" ไม่ได้ช่วยอะไรมากมาย
สิ่งที่เราต้องการทำ :
บางสิ่งเช่นนี้ แต่มีผลผูกพันกับแผนที่ต้นแบบแบบเรียกซ้ำและถูกผูกไว้กับต้นแบบโดยปริยายแทนที่จะต้องทำอย่างชัดเจน
นี่คือความพยายามที่ไร้เดียงสา: ปัญหาคือ "การโทร" ไม่ทราบ "สิ่งที่เรียกว่า" ดังนั้นจึงอาจเป็นฟังก์ชั่นที่เป็นสากล "objectInvokeMethod (object, method)" ซึ่งตรวจสอบว่าวัตถุนั้นมีวิธีการหรือไม่ หรือต้นแบบมีหรือมีต้นแบบจนกว่าจะถึงจุดสิ้นสุดและค้นหามันหรือไม่ (สืบทอดมาจากต้นแบบ) Perl มีเวทย์มนตร์ที่ดีที่จะทำ แต่ฉันจะปล่อยให้สิ่งที่ฉันสามารถลองทำในภายหลัง
อย่างไรก็ตามนี่คือความคิด:
(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'));
})->();
อย่างไรก็ตามหวังว่าใครบางคนจะพบว่าโพสต์นี้มีประโยชน์
my $o = bless {}, $anything;
จะอวยพรวัตถุใน$anything
ชั้นเรียน ในทำนองเดียวกัน{no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
จะสร้างวิธีการตั้งชื่อ 'somesub' $anything
ในชั้นเรียนที่มีชื่อใน ทั้งหมดนี้เป็นไปได้ที่รันไทม์ อย่างไรก็ตาม "เป็นไปได้" ไม่ได้ทำให้เป็นการฝึกฝนที่ยอดเยี่ยมในการใช้รหัสทุกวัน แต่มันมีประโยชน์ในการสร้างระบบวางซ้อนวัตถุเช่นมูหรือหมู่
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
เรียกร้องของฉัน ฉันเดาว่าในที่สุดความกังวลของฉันจะลดลงอย่างเห็นได้ชัดว่ามันใช้งานง่ายน้อยลงในการจัดการ / ตรวจสอบระบบแพ็คเกจที่รันไทม์ แต่จนถึงตอนนี้ฉันไม่ได้แสดงอะไรที่มันไม่สามารถทำได้ ระบบแพคเกจดูเหมือนจะสนับสนุนเครื่องมือทั้งหมดที่จำเป็นในการเพิ่ม / ลบ / ตรวจสอบ / แก้ไขตัวเองในขณะทำงาน
พร้อมกับคำตอบที่ดีจำนวนมากสิ่งที่แยกความแตกต่างของการbless
อ้างอิง -ed ก็คือการที่SV
มันหยิบขึ้นมาเพิ่มเติมFLAGS
( OBJECT
) และSTASH
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'
พิมพ์ด้วยชิ้นส่วน (และไม่เกี่ยวข้องกับสิ่งนี้) ที่ถูกระงับ
SV = IV (0x12d5530) ที่ 0x12d5540 REFCNT = 1 ธง = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) ที่ 0x12a5a68 REFCNT = 1 ธง = (SHAREKEYS) ... SV = IV (0x12a5ce0) ที่ 0x12a5cf0 REFCNT = 1 FLAGS = (IOK, pIOK) IV = 1 --- SV = IV (0x12cb8b8) ที่ 0x12cb8c8 REFCNT = 1 ธง = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) ที่ 0x12c26b0 REFCNT = 1 FLAGS = (OBJECT, SHAREKEYS) STASH = 0x12d5300 "คลาส" ... SV = IV (0x12c26b8) ที่ 0x12c26c8 REFCNT = 1 FLAGS = (IOK, pIOK) IV = 10
เมื่อทราบว่า 1) เป็นวัตถุ 2) เป็นแพคเกจใดและแจ้งให้ทราบถึงการใช้งาน
ตัวอย่างเช่นเมื่อพบการเปลี่ยนแปลงในตัวแปรนั้น ( $obj->name
) จะค้นหา sub ที่มีชื่อนั้นในแพ็คเกจ (หรือลำดับชั้น) วัตถุจะถูกส่งเป็นอาร์กิวเมนต์แรกเป็นต้น
ฉันทำตามความคิดนี้เพื่อเป็นแนวทางในการพัฒนา Perl เชิงวัตถุ
Bless เชื่อมโยงการอ้างอิงโครงสร้างข้อมูลใด ๆ กับคลาส เมื่อพิจารณาว่า Perl สร้างโครงสร้างการสืบทอด (ในรูปแบบของต้นไม้) ได้อย่างไรมันง่ายที่จะใช้ประโยชน์จากโมเดลวัตถุเพื่อสร้าง Objects สำหรับการจัดองค์ประกอบ
สำหรับการเชื่อมโยงนี้เราเรียกว่าวัตถุเพื่อการพัฒนามีอยู่เสมอในใจว่าสถานะภายในของวัตถุและพฤติกรรมของคลาสจะถูกแยกออกจากกัน และคุณสามารถอวยพร / อนุญาตให้มีการอ้างอิงข้อมูลใด ๆ ที่จะใช้พฤติกรรมแพคเกจ / คลาส เนื่องจากแพคเกจสามารถเข้าใจสถานะ "อารมณ์" ของวัตถุ
ตัวอย่างเช่นหากคุณมั่นใจได้ว่าวัตถุ Bug ใด ๆ จะเป็นแฮชที่มีความสุขคุณสามารถ (ในที่สุด!) กรอกรหัสที่หายไปในวิธี 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";
}
ตอนนี้เมื่อใดก็ตามที่เรียกใช้เมธอด print_me ผ่านการอ้างอิงแฮชใด ๆ ที่ได้รับพรเข้าสู่คลาส Bug ตัวแปร $ self จะแยกการอ้างอิงที่ถูกส่งผ่านเป็นอาร์กิวเมนต์แรกจากนั้นคำสั่งพิมพ์จะเข้าถึงรายการต่างๆของแฮชที่ได้รับพร