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

时间:2009-11-13 19:26:09

标签: perl

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

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

5 个答案:

答案 0 :(得分:52)

ref()

  

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

     

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

答案 1 :(得分:43)

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

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

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等),请参阅此问题:How can I get Perl's ref() function to return REF, IO, and LVALUE?perldoc perlref

注意:您应该使用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

答案 2 :(得分:18)

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

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

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

如果您想知道该标量是否实际上只是表现为标量但与某个类绑定,请尝试tied。如果你得到一个对象,继续检查。

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

如果您需要做一些更有趣的事情,可以使用Params::Validate等模块。

答案 3 :(得分:4)

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

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 :(得分:2)

在某些时候,我在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,而不是它是否是一个哈希引用。

我不喜欢这种实现。