如何判断Perl变量中的值类型?
$x
可能是标量,对数组的引用或对哈希(或其他东西)的引用。
答案 0 :(得分:52)
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上阅读了一个相当令人信服的论点,即使用ref
或reftype
测试标量的类型是一个坏主意。我不记得是谁提出了这个想法,或者链接。遗憾。
关键是在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,而不是它是否是一个哈希引用。
我不喜欢这种实现。