我如何知道Perlvariables中的什么types的值?

我如何知道Perlvariables中的什么types的值?

$x可能是一个标量,一个数组的引用或一个散列的引用(或者其他的东西)。

ref() :

Perl提供了ref()函数,以便在引用引用之前检查引用types。

通过使用ref()函数,您可以保护程序代码,当使用错误的引用types时,可以避免引用variables产生错误。

$x总是一个标量。 提示是sigil $ :以$开头的任何variables(或其他types的取消引用)都是标量。 (有关数据types的更多信息,请参阅perldoc perldata 。)

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

 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 

有关其他types的引用(如coderef,arrayref等)的更多信息,请参阅此问题: 如何获得Perl的ref()函数以返回REF,IO和LVALUE? 和perldoc perlref 。

注意:你不应该使用ref来实现带有祝福对象的代码分支(例如$ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined"; ) – if你需要根据一个variables的types做出任何决定,使用isa (即if ($a->isa("My::Foo") { ...if ($a->can("foo") { ... )。另见多态 。

一个标量总是包含一个元素。 无论标量variables是什么,总是一个标量。 一个引用是一个标量值。

如果你想知道它是否是一个参考,你可以使用ref 。 如果你想知道引用types,你可以使用Scalar :: Util的reftype例程。

如果你想知道它是否是一个对象,你可以使用Scalar :: Util的blessed例程。 尽pipe如此,你永远不应该关心那个幸运的包装是什么。 UNIVERSAL有一些方法可以告诉你一个对象:如果你想检查它是否有你要调用的方法,使用can ; 如果你想看到它从某种东西inheritance,使用isa ; 如果你想看到它的对象处理angular色,使用DOES

如果你想知道这个标量实际上只是一个标量,但是和一个类绑定在一起,那就试着tied一下。 如果你得到一个对象,继续检查。

如果你想知道它是否看起来像一个数字,你可以使用Scalar :: Util中的 looks_like_number 。 如果它看起来不像一个数字,它不是一个参考,它是一个string。 但是,所有简单的值都可以是string。

如果你需要做更多的事情,你可以使用一个模块,例如Params :: Validate 。

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

 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 

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

你的types库:

 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

维护。

在某些时候,我读了一个关于Perlmonks的合理令人信服的论点,用refreftypetesting标量的types是一个坏主意。 我不记得谁提出了这个想法,或者说这个联系。 抱歉。

关键在于,在Perl中有许多机制可以使得像任何你想要的那样做一个给定的标量行为。 如果你tie一个文件句柄,使得它像一个散列,用reftypetesting会告诉你,你有一个filehanle。 它不会告诉你,你需要使用它像一个哈希。

所以,争论起来了,最好用鸭子打字找出variables是什么。

代替:

 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; } 

在大多数情况下,我并没有这样做,但在某些情况下,我有。 至于何时这种方法是适当的,我仍然在考虑。 我想我会把这个概念提出来进一步讨论。 我很想看到评论。

更新

我意识到我应该就这个方法提出我的想法。

这种方法有处理任何你扔在它的优势。

它有麻烦,有点奇怪的缺点。 在一些代码中绊倒这将使我发出一个很大的“跆拳道”。

我喜欢testing一个标量是否 hash-ref的想法,而不pipe它是否是一个hash参考。

我不喜欢这个实现。