Answers:
bless
将引用与包关联。
引用的内容无关紧要,可以是哈希(最常见的情况),数组(不太常见),标量(通常表示从里到外的对象),正则表达式,子例程或TYPEGLOB(有关有用的示例,请参见Damian Conway的《面向对象的Perl:概念和编程技术的综合指南》一书),甚至是对文件或目录句柄的引用(最不常见的情况)。
bless
-ing 的作用是,它允许您将特殊语法应用于受祝福的引用。
例如,如果存储了一个受祝福的引用$obj
(bless
与包“ Class” 关联),$obj->foo(@args)
则将调用一个子例程foo
并将该引用作为$obj
后面的其余参数(@args
)作为第一个参数传递。该子例程应在包“类”中定义。如果foo
软件包“ Class”中没有子例程,@ISA
则将搜索其他软件包列表(从软件包“ Class”中的数组中获取)并调用foo
找到的第一个子例程。
此函数告诉REF引用的实体,它现在是CLASSNAME包中的对象,如果省略CLASSNAME,则为当前包中的对象。建议使用bless的两个参数形式。
范例:
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的bless函数将任何引用与包中的所有函数相关联。
我们为什么需要这个?
让我们开始用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年必须以特定顺序以动态语言编写属性是没有意义的),并返回具有这些属性的哈希表,或者如果您忘记放置new关键字,则该函数将返回整个全局上下文(例如,浏览器中的窗口或nodejs中的global)。
Perl没有“ this”,“ new”或“ class”,但是它仍然可以具有类似的功能。我们既没有构造函数,也没有原型,但是我们将能够随意创建新动物并修改其单独属性。
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
现在,我们有一个问题:如果我们希望动物自己表演声音而不是我们打印他们的声音是什么,该怎么办?也就是说,我们需要一个函数performSound来打印动物自己的声音。
做到这一点的一种方法是教每个单独的动物如何做声音。这意味着每个Cat都有其自己的重复功能来执行。
# 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都会作为一个全新的函数对象放入。10000动物意味着10000表演声音。我们希望有一个功能PerformSound,供所有动物使用,它们会查找自己的声音并进行打印。
(() => {
'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相似的地方。
JavaScript的new运算符不是可选的,没有它,对象方法内部的“ this”会破坏全局范围:
(() => {
// '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没有“ this”,“ class”或“ new”。将一个对象祝福给一个包将为其提供对该包的引用,并且当它调用该包中的函数时,它们的参数将偏移1个槽,并且第一个参数($ _ [0]或shift)将等于javascript的“ this”。反过来,您可以在某种程度上模拟JavaScript的原型模型。
不幸的是,由于您需要每个“类”都有自己的包,因此(据我所知)无法在运行时创建“新类”,而在javascript中,根本不需要包,例如“ new”关键字组成一个匿名哈希图,供您在运行时用作程序包,您可以在其中动态添加或删除函数。
有一些Perl库,例如Moose,创建了自己的方法来弥合这种表达上的限制。
为什么会感到困惑?:
因为包装。我们的直觉告诉我们将对象绑定到包含其原型的哈希图。这使我们可以像JavaScript一样在运行时创建“程序包”。Perl不具有这种灵活性(至少不是内置的,您必须发明它或从其他模块中获得它),从而阻碍了运行时的表达能力。称其为“祝福”并没有多大好处。
我们想做什么:
像这样的东西,但是必须绑定到原型映射递归,并且隐式绑定到原型,而不是必须显式地绑定。
这是一个幼稚的尝试:问题是“调用”不知道“调用什么”,因此它也可能是一个通用的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
。这在运行时都是可能的。但是,“可能”并不是每天使用代码的好习惯。但是在构建对象覆盖系统(例如Moose或Moo)时很有用。
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 标志=(IOK,pIOK) IV = 1 --- SV = IV(0x12cb8b8)在0x12cb8c8 REFCNT = 1 标志=(PADMY,ROK) RV = 0x12c26b0 SV = PVHV(0x12aba00)在0x12c26b0 REFCNT = 1 标志=(对象,密钥) STASH = 0x12d5300“类” ... SV = IV(0x12c26b8)在0x12c26c8 REFCNT = 1 标志=(IOK,pIOK) IV = 10
这样就知道1)它是一个对象2)它属于哪个包,并且这通知了它的使用。
例如,在遇到对该变量的取消引用($obj->name
)时,将在包(或层次结构)中搜索具有该名称的子对象,将该对象作为第一个参数传递,依此类推。
例如,如果您可以确定任何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";
}
现在,每当通过引用任何已加到Bug类中的哈希值的引用调用print_me方法时,$ self变量都会提取作为第一个参数传递的引用,然后print语句访问加倍的哈希值的各个条目。