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

时间:2022-12-30 07:21:34

How do I tell what type of value is in a Perl variable?

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

$x might be a scalar, a ref to an array or a ref to a hash (or maybe other things).

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

5 个解决方案

#1


49  

ref():

REF():

Perl provides the ref() function so that you can check the reference type before dereferencing a reference...

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

By using the ref() function you can protect program code that dereferences variables from producing errors when the wrong type of reference is used...

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

#2


42  

$x is always a scalar. The hint is the sigil $: any variable (or dereferencing of some other type) starting with $ is a scalar. (See perldoc perldata for more about data types.)

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

A reference is just a particular type of scalar. The built-in function ref will tell you what kind of reference it is. On the other hand, if you have a blessed reference, ref will only tell you the package name the reference was blessed into, not the actual core type of the data (blessed references can be hashrefs, arrayrefs or other things). You can use Scalar::Util 's reftype will tell you what type of reference it is:

引用只是一种特殊类型的标量。内置函数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";

...produces the output:

...产生输出:

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

For more information about the other types of references (e.g. coderef, arrayref etc), see this question: How can I get Perl's ref() function to return REF, IO, and LVALUE? and perldoc perlref.

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

Note: You should not use ref to implement code branches with a blessed object (e.g. $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) -- if you need to make any decisions based on the type of a variable, use isa (i.e if ($a->isa("My::Foo") { ... or if ($a->can("foo") { ...). Also see polymorphism.

注意:你不应该使用ref来实现带有祝福对象的代码分支(例如$ ref($ a)eq“My :: Foo”?说“是一个Foo对象”:说“foo not defined”;) - 如果你需要根据变量的类型做出任何决定,使用isa(即if($ a-> isa(“My :: Foo”){...或if($ a-> can can(“foo”)) {...)。另见多态性。

#3


16  

A scalar always holds a single element. Whatever is in a scalar variable is always a scalar. A reference is a scalar value.

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

If you want to know if it is a reference, you can use ref. If you want to know the reference type, you can use the reftype routine from Scalar::Util.

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

If you want to know if it is an object, you can use the blessed routine from Scalar::Util. You should never care what the blessed package is, though. UNIVERSAL has some methods to tell you about an object: if you want to check that it has the method you want to call, use can; if you want to see that it inherits from something, use isa; and if you want to see it the object handles a role, use DOES.

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

If you want to know if that scalar is actually just acting like a scalar but tied to a class, try tied. If you get an object, continue your checks.

如果你想知道那个标量是否实际上只是像标量那样但是与一个类绑在一起,那就试试看吧。如果您有对象,请继续检查。

If you want to know if it looks like a number, you can use looks_like_number from Scalar::Util. If it doesn't look like a number and it's not a reference, it's a string. However, all simple values can be strings.

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

If you need to do something more fancy, you can use a module such as Params::Validate.

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

#4


4  

I like polymorphism instead of manually checking for something:

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

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

This is much more powerful than manual checking, as you can reuse your "checks" like you would any other type constraint. That means when you want to handle arrays, hashes, and even numbers less than 42, you just write a constraint for "even numbers less than 42" and add a new multimethod for that case. The "calling code" is not affected.

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

Your type library:

你的类型库:

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

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

Then make Foo support this (in that class definition):

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

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

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

Then Foo->new->foo(40) prints arg is an even number less than 42 instead of arg is something else.

然后Foo-> new-> foo(40)打印arg是一个小于42的偶数而不是arg是别的东西。

Maintainable.

维护。

#5


2  

At some point I read a reasonably convincing argument on Perlmonks that testing the type of a scalar with ref or reftype is a bad idea. I don't recall who put the idea forward, or the link. Sorry.

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

The point was that in Perl there are many mechanisms that make it possible to make a given scalar act like just about anything you want. If you tie a filehandle so that it acts like a hash, the testing with reftype will tell you that you have a filehanle. It won't tell you that you need to use it like a hash.

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

So, the argument went, it is better to use duck typing to find out what a variable is.

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

Instead of:

代替:

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

You should do something like this:

你应该做这样的事情:

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

For the most part I don't actually do this, but in some cases I have. I'm still making my mind up as to when this approach is appropriate. I thought I'd throw the concept out for further discussion. I'd love to see comments.

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

Update

更新

I realized I should put forward my thoughts on this approach.

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

This method has the advantage of handling anything you throw at it.

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

It has the disadvantage of being cumbersome, and somewhat strange. Stumbling upon this in some code would make me issue a big fat 'WTF'.

它的缺点是笨重,有点奇怪。在一些代码中磕磕绊绊会让我发出一个很大的'WTF'。

I like the idea of testing whether a scalar acts like a hash-ref, rather that whether it is a hash ref.

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

I don't like this implementation.

我不喜欢这个实现。

#1


49  

ref():

REF():

Perl provides the ref() function so that you can check the reference type before dereferencing a reference...

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

By using the ref() function you can protect program code that dereferences variables from producing errors when the wrong type of reference is used...

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

#2


42  

$x is always a scalar. The hint is the sigil $: any variable (or dereferencing of some other type) starting with $ is a scalar. (See perldoc perldata for more about data types.)

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

A reference is just a particular type of scalar. The built-in function ref will tell you what kind of reference it is. On the other hand, if you have a blessed reference, ref will only tell you the package name the reference was blessed into, not the actual core type of the data (blessed references can be hashrefs, arrayrefs or other things). You can use Scalar::Util 's reftype will tell you what type of reference it is:

引用只是一种特殊类型的标量。内置函数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";

...produces the output:

...产生输出:

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

For more information about the other types of references (e.g. coderef, arrayref etc), see this question: How can I get Perl's ref() function to return REF, IO, and LVALUE? and perldoc perlref.

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

Note: You should not use ref to implement code branches with a blessed object (e.g. $ref($a) eq "My::Foo" ? say "is a Foo object" : say "foo not defined";) -- if you need to make any decisions based on the type of a variable, use isa (i.e if ($a->isa("My::Foo") { ... or if ($a->can("foo") { ...). Also see polymorphism.

注意:你不应该使用ref来实现带有祝福对象的代码分支(例如$ ref($ a)eq“My :: Foo”?说“是一个Foo对象”:说“foo not defined”;) - 如果你需要根据变量的类型做出任何决定,使用isa(即if($ a-> isa(“My :: Foo”){...或if($ a-> can can(“foo”)) {...)。另见多态性。

#3


16  

A scalar always holds a single element. Whatever is in a scalar variable is always a scalar. A reference is a scalar value.

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

If you want to know if it is a reference, you can use ref. If you want to know the reference type, you can use the reftype routine from Scalar::Util.

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

If you want to know if it is an object, you can use the blessed routine from Scalar::Util. You should never care what the blessed package is, though. UNIVERSAL has some methods to tell you about an object: if you want to check that it has the method you want to call, use can; if you want to see that it inherits from something, use isa; and if you want to see it the object handles a role, use DOES.

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

If you want to know if that scalar is actually just acting like a scalar but tied to a class, try tied. If you get an object, continue your checks.

如果你想知道那个标量是否实际上只是像标量那样但是与一个类绑在一起,那就试试看吧。如果您有对象,请继续检查。

If you want to know if it looks like a number, you can use looks_like_number from Scalar::Util. If it doesn't look like a number and it's not a reference, it's a string. However, all simple values can be strings.

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

If you need to do something more fancy, you can use a module such as Params::Validate.

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

#4


4  

I like polymorphism instead of manually checking for something:

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

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

This is much more powerful than manual checking, as you can reuse your "checks" like you would any other type constraint. That means when you want to handle arrays, hashes, and even numbers less than 42, you just write a constraint for "even numbers less than 42" and add a new multimethod for that case. The "calling code" is not affected.

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

Your type library:

你的类型库:

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

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

Then make Foo support this (in that class definition):

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

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

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

Then Foo->new->foo(40) prints arg is an even number less than 42 instead of arg is something else.

然后Foo-> new-> foo(40)打印arg是一个小于42的偶数而不是arg是别的东西。

Maintainable.

维护。

#5


2  

At some point I read a reasonably convincing argument on Perlmonks that testing the type of a scalar with ref or reftype is a bad idea. I don't recall who put the idea forward, or the link. Sorry.

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

The point was that in Perl there are many mechanisms that make it possible to make a given scalar act like just about anything you want. If you tie a filehandle so that it acts like a hash, the testing with reftype will tell you that you have a filehanle. It won't tell you that you need to use it like a hash.

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

So, the argument went, it is better to use duck typing to find out what a variable is.

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

Instead of:

代替:

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

You should do something like this:

你应该做这样的事情:

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

For the most part I don't actually do this, but in some cases I have. I'm still making my mind up as to when this approach is appropriate. I thought I'd throw the concept out for further discussion. I'd love to see comments.

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

Update

更新

I realized I should put forward my thoughts on this approach.

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

This method has the advantage of handling anything you throw at it.

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

It has the disadvantage of being cumbersome, and somewhat strange. Stumbling upon this in some code would make me issue a big fat 'WTF'.

它的缺点是笨重,有点奇怪。在一些代码中磕磕绊绊会让我发出一个很大的'WTF'。

I like the idea of testing whether a scalar acts like a hash-ref, rather that whether it is a hash ref.

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

I don't like this implementation.

我不喜欢这个实现。