5

我遇到了这段代码,但我不明白。

$type->isa('UNIVERSAL')
  or eval "require $type"
    or croak $@;
  1. 我已经perldoc参考并且我知道子程序isa()正在检查是否$type已被祝福包UNIVERSAL。但实际上,所有 Perl 类都是UNIVERSAL,对吧?

  2. 我对这两个or术语感到困惑。他们的意思是什么?

4

4 回答 4

3

链式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 $@;

就是这样一个链条。评估按以下顺序进行:

  1. $type->isa('UNIVERSAL')
  2. eval "require $type"
  3. 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_fromisa

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") ? “加载”:“未加载”
加载
于 2013-05-22T12:51:21.547 回答
3
  1. isa在这里被滥用来检查给定的包是否已经用requireor导入usePackage->isa('UNIVERSAL')如果 packagecagePackage存在,将返回 true,否则返回 false。

  2. or正在使用short circuit容量。在表达式中,如果具有真值a or bb则不会被评估。a如果术语是变量,这没有区别,但如果它们是具有副作用的子例程调用,那么它就成为if语句的替代方案。

    有问题的语句等效于此代码

    unless ($type->isa('UNIVERSAL')) {
      unless (eval "require $type") {
        croak $@;
      }
    }
    

    整体效果是检查变量指定的模块是否$type已经导入。如果不是,则require用于导入它,croak如果失败则调用它。

于 2013-05-22T07:30:10.560 回答
2

问题 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 $@;
    }
 }
于 2013-05-22T06:56:54.993 回答
1

with , perl 只有在左侧执行返回非假值or时才会执行右侧语句

于 2013-05-22T06:57:49.467 回答