it-swarm.dev

如何判断Perl变量中的值类型?

如何判断Perl变量中的值类型?

$x可能是标量,对数组的引用或对哈希(或其他东西)的引用。

55
pm100

ref()

Perl提供ref()函数,以便您可以在取消引用引用之前检查引用类型...

通过使用ref()函数,您可以保护程序代码,当使用错误的引用类型时,解引用变量不会产生错误...

52
ctd

$x始终是标量。提示是sigil $:从$开始的任何变量(或其他类型的解除引用)都是标量。 (有关数据类型的更多信息,请参阅 perldoc perldata 。)

引用只是一种特殊类型的标量。内置函数 ref 将告诉你它是什么类型的引用。另一方面,如果你有一个受祝福的引用,ref只会告诉你引用的包名称,而不是数据的实际核心类型(祝福引用可以是hashrefs,arrayrefs或其他东西)。您可以使用 Scalar :: Utilreftype将告诉您它是什么类型的引用:

use Scalar::Util qw(reftype);

my $x = bless {}, 'My::Foo';
my $y = { };

print "type of x: " . ref($x) . "\n";
print "type of y: " . ref($y) . "\n";
print "base type of x: " . reftype($x) . "\n";
print "base type of y: " . reftype($y) . "\n";

...产生输出:

type of x: My::Foo
type of y: HASH
base type of x: HASH
base type of y: HASH

有关其他类型引用的更多信息(例如coderef,arrayref等),请参阅以下问题: 如何让Perl的ref()函数返回REF,IO和LVALUE?perldoc perlref

注意:您应该 not use ref来实现带有祝福对象的代码分支(例如$ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) - 如果您需要根据变量的类型做出任何决定,请使用 isa (即if ($a->isa("My::Foo") { ...if ($a->can("foo") { ...)。另见 polymorphism

43
Ether

标量始终包含单个元素。标量变量中的任何内容始终是标量。引用是标量值。

如果你想知道它是否是参考,你可以使用 ref 。如果您想知道引用类型,可以使用 Scalar :: Util 中的reftype例程。

如果你想知道它是否是一个对象,你可以使用 Scalar :: Util 中的blessed例程。不过,你永远不应该关心受祝福的包裹是什么。 UNIVERSAL有一些方法可以告诉你一个对象:如果你想检查它是否有你要调用的方法,请使用can;如果你想看到它继承自某些东西,请使用isa;如果你想看到对象处理一个角色,请使用DOES

如果你想知道这个标量是否实际上只是一个标量但是与一个类绑定,请尝试 tied 。如果您有对象,请继续检查。

如果你想知道它是否看起来像一个数字,你可以使用 Scalar :: Util 中的looks_like_number。如果它看起来不像数字并且它不是引用,那么它就是一个字符串。但是,所有简单值都可以是字符串。

如果你需要做一些更奇特的事情,可以使用 Params :: Validate 等模块。

18
brian d foy

我喜欢多态而不是手动检查某些东西:

use MooseX::Declare;

class Foo {
    use MooseX::MultiMethods;

    multi method foo (ArrayRef $arg){ say "arg is an array" }
    multi method foo (HashRef $arg) { say "arg is a hash" }
    multi method foo (Any $arg)     { say "arg is something else" }
}

Foo->new->foo([]); # arg is an array
Foo->new->foo(40); # arg is something else

这比手动检查要强大得多,因为您可以像使用任何其他类型约束一样重复使用“检查”。这意味着当你想处理小于42的数组,散列和偶数时,你只需为“偶数小于42”写一个约束,并为这种情况添加一个新的多方法。 “呼叫代码”不受影响。

你的类型库:

package MyApp::Types;
use MooseX::Types -declare => ['EvenNumberLessThan42'];
use MooseX::Types::Moose qw(Num);

subtype EvenNumberLessThan42, as Num, where { $_ < 42 && $_ % 2 == 0 };

然后让Foo支持这个(在该类定义中):

class Foo {
    use MyApp::Types qw(EvenNumberLessThan42);

    multi method foo (EvenNumberLessThan42 $arg) { say "arg is an even number less than 42" }
}

然后Foo->new->foo(40)打印arg is an even number less than 42而不是arg is something else

维护。

4
jrockway

在某些时候,我在Perlmonks上读到了一个相当令人信服的论点,即用refreftype测试标量的类型是一个坏主意。我不记得是谁提出了这个想法,或者链接。抱歉。

关键在于,在Perl中有许多机制可以使给定的标量行为像你想要的任何东西一样。如果你tie一个文件句柄,它就像一个哈希,用reftype测试会告诉你你有一个文件句。它不会告诉您需要像哈希一样使用它。

所以,争论说,最好使用duck typing来找出变量是什么。

代替:

sub foo {
    my $var = shift;
    my $type = reftype $var;

    my $result;
    if( $type eq 'HASH' ) {
        $result = $var->{foo};
    }
    elsif( $type eq 'ARRAY' ) {
        $result = $var->[3];
    }
    else {
        $result = 'foo';
    }

    return $result;
}

你应该做这样的事情:

sub foo {
    my $var = shift;
    my $type = reftype $var;

    my $result;

    eval {
        $result = $var->{foo};
        1; # guarantee a true result if code works.
    }
    or eval { 
        $result = $var->[3];
        1;
    }
    or do {
        $result = 'foo';
    }

    return $result;
}

在大多数情况下,我实际上并没有这样做,但在某些情况下,我有。我还在考虑这种方法何时合适。我以为我会把这个概念抛出来进一步讨论。我很乐意看到评论。

更新

我意识到我应该提出我对这种方法的看法。

这种方法的优点是可以处理你抛出的任何东西。

它的缺点是笨重,有点奇怪。在一些代码中绊倒这将使我发出一个很大的'WTF'。

我喜欢测试标量 是否像 hash-ref一样,而不是它是否是散列引用。

我不喜欢这个实现。

2
daotoad