我遇到了这段代码,但我不明白。
$type->isa('UNIVERSAL')
or eval "require $type"
or croak $@;
我已经
perldoc
参考并且我知道子程序isa()
正在检查是否$type
已被祝福包UNIVERSAL
。但实际上,所有 Perl 类都是UNIVERSAL
,对吧?我对这两个
or
术语感到困惑。他们的意思是什么?
我遇到了这段代码,但我不明白。
$type->isa('UNIVERSAL')
or eval "require $type"
or croak $@;
我已经perldoc
参考并且我知道子程序isa()
正在检查是否$type
已被祝福包UNIVERSAL
。但实际上,所有 Perl 类都是UNIVERSAL
,对吧?
我对这两个or
术语感到困惑。他们的意思是什么?
or
旋律Perl 的or
操作符的工作方式与 Perl 的操作符一样||
,但优先级很低,也就是说,它与周围代码的绑定有多“紧密”。逻辑或在 Perl 短路中,即一旦操作数评估为真,评估就会停止。
Perl 中的一个常见习惯用法使用短路来进行流控制,通常与错误处理有关,如
open my $fh, "<", $path or die "$0: open: $!";
成功时,open
返回一个真值。or
当它的至少一个操作数为真时,结果为真,所以在成功的open
情况下,perl 不会,die
因为右手操作数 toor
没有被计算。
运营商链or
可以任意扩展。这对于指定默认值很有用。在简单的情况下,这看起来像
my $name = shift or "Bruce";
如果您有多个后备案例,请将它们按您想要尝试的顺序排列。
my $name = shift or given_name $family_name or "Bruce";
第一个成功的案例获胜。
编码
$type->isa('UNIVERSAL')
or eval "require $type"
or croak $@;
就是这样一个链条。评估按以下顺序进行:
$type->isa('UNIVERSAL')
eval "require $type"
croak $@
如果$type
是已经存在的包的名称,则忽略第 2 步和第 3 步。
到达第 2 步意味着$type
未加载,所以现在代码尝试加载require
它。如果require
成功,则忽略第 3 步。
到达第 3 步意味着该包不存在且无法加载。在这种情况下,croak
输出失败的原因并退出程序。
当作为方法调用时,isa
具有三种形式:
$obj->isa( TYPE )
CLASS->isa( TYPE )
eval { VAL->isa( TYPE ) }
第三个是在这里不相关的通用包罗万象。首先是您在问题中描述的内容。
第二个被记录为
当用作类方法(
CLASS->isa( TYPE )
),有时称为静态方法)时,isa
如果CLASS继承自(或本身)包TYPE的名称或继承自包TYPE ,则返回 true 。
来自 SOAP::WSDL::XSD::TypeLib::ComplexTypeor
的链 in之前的行是::_factory
my $type = $CLASSES_OF{ $class }->{ $name }
or croak "No class given for $name";
这里, 的值$type
是一个字符串,因此isa
检查名为的类是否$type
继承自 UNIVERSAL。正如您在问题中指出的那样,所有类都继承自 UNIVERSAL。
发生什么了?如果isa
失败,则代码调用require
,因此isa
检查的目的必须是确定是否已加载给定的类。
请记住,Perl 类和 Perl 包密切相关。要存在一个类,必须存在同名的包。非破坏性地检测一个包是否存在是很棘手的。直接探测藏匿处%Foo::Bar::Baz::Quux::
自动激活。Perl 存储分层存储,所以你必须像在
sub package_exists {
my($pkg) = @_;
$pkg =~ s/::$//;
my @parts = split /::/, $pkg;
my $stash = $main::{"main::"};
while (@parts) {
my $subpkg = shift(@parts) . "::";
return unless exists $stash->{$subpkg};
$stash = $stash->{$subpkg};
}
$stash;
}
成功require
修改了一个名为%INC的特殊哈希。我不确定为什么代码不检查%INC
。也许某些类型是在 require 机制之外加载的,或者作者可能对这个令人愉快的小技巧感到满意。
考虑实现 的sv_derived_from
源isa
。
bool
Perl_sv_derived_from_pvn(pTHX_ SV *sv,
const char *const name, const STRLEN len,
U32 flags)
{
dVAR;
HV *stash;
PERL_ARGS_ASSERT_SV_DERIVED_FROM_PVN;
SvGETMAGIC(sv);
if (SvROK(sv)) {
const char *type;
sv = SvRV(sv);
type = sv_reftype(sv,0);
if (type && strEQ(type,name))
return TRUE;
stash = SvOBJECT(sv) ? SvSTASH(sv) : NULL;
}
else {
stash = gv_stashsv(sv, 0);
if (!stash)
stash = gv_stashpvs("UNIVERSAL", 0);
}
return stash ? isa_lookup(stash, name, len, flags) : FALSE;
}
请记住,我们正在调用此代码
"SomeClass"->isa("UNIVERSAL")
sosv
包含字符串"SomeClass"
。SvROK
检查是否sv
是引用,字符串不是,所以我们总是在else
分支中。
最后, 的值stash
将始终为 non- NULL
:如果已加载,则指向包的符号表,否则指向 UNIVERSAL。查找已加载的包成功,否则失败。自己试试:
$ perl -le 'print "strict"->isa("UNIVERSAL") ? “加载”:“未加载” 未加载 $ perl -Mstrict -le 'print "strict"->isa("UNIVERSAL") ? “加载”:“未加载” 加载
isa
在这里被滥用来检查给定的包是否已经用require
or导入use
。Package->isa('UNIVERSAL')
如果 packagecagePackage
存在,将返回 true,否则返回 false。
or
正在使用short circuit
容量。在表达式中,如果具有真值a or b
,b
则不会被评估。a
如果术语是变量,这没有区别,但如果它们是具有副作用的子例程调用,那么它就成为if
语句的替代方案。
有问题的语句等效于此代码
unless ($type->isa('UNIVERSAL')) {
unless (eval "require $type") {
croak $@;
}
}
整体效果是检查变量指定的模块是否$type
已经导入。如果不是,则require
用于导入它,croak
如果失败则调用它。
问题 1
UNIVERSAL 是基类Perl 中所有类的基类。
问题2
or
在 Perl 中是低优先级运算符,意思是
A or B
如果为真A
,则为真,或者如果B
为真,则意味着如果两个条件中的任何一个为真。A
上面的表达式在之前计算or
,前提是A
运算符的优先级高于or
。这很可能是因为or
(不像||
) 在 Perl 中的优先级最低。在 Perl 中,给定表达式
A or B or C;
B
仅当为 false 时才被处理(评估),并且仅当为falseA
时才被处理
C
(评估)。B
实际上,在您的情况下,如果$type->isa('UNIVERSAL')
为true,Perl 会停在这里并认为整个表达式为true。如果false,它将评估eval "require $type"
:如果 true 整个表达式为 true,如果false,它最终评估croak $@
。
这是一种快速的方法
if ( ! $type->isa('UNIVERSAL')) {
if ( ! eval "require $type") {
croak $@;
}
}
with , perl 只有在左侧执行返回非假值or
时才会执行右侧语句