【问题标题】:Detecting element types in a mixed array检测混合数组中的元素类型
【发布时间】:2017-01-19 11:08:26
【问题描述】:

我正在使用一些代码,该代码具有一个包含数组引用作为参数之一的子例程。这个传入数组中的元素可以是小数组或字符串。

我想确定每个元素的类型以便做一些特定的事情(即如果元素是数组,则通过索引进一步深入,如果元素是字符串,则使用字符串)

我尝试使用ref 函数来询问每个数组元素。它似乎适用于数组元素,但如果元素是字符串,我期待 ref 返回 SCALAR。然而ref() 似乎什么也没返回。我究竟做错了什么?我认为ref() 会返回一些东西。

这里是一些示例代码:

my @array = ("string1", 
             ["ele1_arraystr1", "ele1_arraystr2"], 
             "string2", 
             ["ele4_arraystr1", "ele4_arraystr2"], 
             "etc");
my $tmp;
&foobar( 30, 20, \@array);

sub foobar {
    my($var1, $var2, $array_ref) = @_;
    foreach $element (@$array_ref) {
        my $tmp = ref($element);
        print "Array element type: $tmp\n";
        if ($tmp eq 'ARRAY') {
            print "  ARRAY: $element->[1]\n";

        } elsif ($tmp eq 'SCALAR') {
            print "  SCALAR: $element\n";
        } else {
            print "  Unexpected type: $tmp\n";
       }
    }
 }

输出看起来像这样:

ARRAY element test:
Array element type: 
  Unexpected type: 
Array element type: ARRAY
  ARRAY: ele1_arraystr2
Array element type: 
  Unexpected type: 
Array element type: ARRAY
  ARRAY: ele4_arraystr2
Array element type: 
  Unexpected type: 

【问题讨论】:

    标签: arrays perl ref scalar


    【解决方案1】:

    ref 如果其参数不是引用,则返回一个空字符串。文档说

    如果 EXPR 是引用,则返回非空字符串,否则返回空字符串。返回的值取决于引用所引用的事物的类型。

    下面的列表,包括SCALAR,是引用可以指向的类型。

    所以当它有一个字符串时,它会返回一个空字符串,结果为假。如果你要知道它是ARRAY 或字符串,你可以这样做

    if (ref($element) eq 'ARRAY') {
        # process the arrayref
    }
    else { 
        # process the string
    }
    

    更好地检查字符串(假),就像你做的那样,以便能够检测任何其他类型

    my $ref_type = ref($element);
    if ($ref_type eq 'ARRAY') {
        # process arrayref
    }
    elsif (not $ref_type) {  
        # process string
    }
    else { print "Got type $ref_type\n" }
    

    【讨论】:

    • 我把 elsif 留在了那里,因为将来可能会有其他类型的引用。根据这个答案和下面的答案,我看到了我现在缺少的东西。显然,因为字符串本身不是引用,所以 ref() 返回零或 false,而不是类型字符串。谢谢。
    • @BentChainRing 好的,很好的做法。然后您可以通过elsif (not $tmp) { } 测试字符串,因为这是ref 返回错误(空字符串)的唯一情况。然后,您将拥有 else { } 来检测未来对其他类型的引用(或以特定的 elsif 序列拼出它们)。
    • @BentChainRing 再次感谢您的评论,这当然是更好的做法。我被问题的陈述误导了,它只是两者之一(并想传达基本观点)。调整答案。
    【解决方案2】:

    所有这些都记录在perldoc perlfunc under ref

    ref 如果其参数不是引用,则返回 false 值。只有当参数是标量的引用

    时,它才会返回SCALAR

    您可能还需要知道,对于 blessed 数据(Perl 对象)的引用,ref 返回数据已被祝福的 class,并且不是基础数据类型。如果需要区分两者,那么核心Scalar::Util模块提供blessed,返回数据已经被加持到的类,reftype,同样返回底层数据的类型如ref

    你可以让你的foobar递归处理一个无限嵌套的数据结构,像这样

    use strict;
    use warnings 'all';
    use feature 'say';
    
    my @array = (
        "string1", [ "ele1_arraystr1", "ele1_arraystr2" ],
        "string2", [ "ele4_arraystr1", "ele4_arraystr2" ],
        "etc",     [ "etc1", "etc2" ]
    );
    
    foobar(\@array);
    
    sub foobar {
        my ($val, $indent) = (@_);
        $indent //= 0;
        my $ref = ref $val;
    
        if ( $ref eq 'ARRAY' ) {
            foobar($_, $indent+1) for @$val;
        }
        elsif ( not $ref ) {
            say '  ' x $indent, $val;
        }
    }
    

    输出

      string1
        ele1_arraystr1
        ele1_arraystr2
      string2
        ele4_arraystr1
        ele4_arraystr2
      etc
        etc1
        etc2
    

    或者,如果您的数组总是由交替的字符串和数组引用组成,您可能会发现将其分配给散列会更容易。这将使用字符串作为哈希键,并将其对应的数组引用作为哈希值

    这段代码显示了这个想法。我已经使用Data::Dump 来显示生成的数据结构

    my %data = @array;
    
    use Data::Dump;
    dd \%data;
    

    输出

    {
      etc => ["etc1", "etc2"],
      string1 => ["ele1_arraystr1", "ele1_arraystr2"],
      string2 => ["ele4_arraystr1", "ele4_arraystr2"],
    }
    

    【讨论】:

    • 下次我回到这段代码时可能会使用该例程逻辑。我现在在短期内还有其他一些优先事项。感谢您指出,正如另一张海报所做的那样, ref() 没有将独立字符串视为引用,并且 ref() 仅返回 reference 的类型。
    • @BentChainRing:是的,这就是运营商的名字!您可能还需要知道,对于 blessed 数据(Perl 对象)的引用,ref 返回数据已被祝福的 class,而不是底层数据类型。如果需要区分这两者,那么Scalar::Util模块提供blessed,返回数据已被加持到的类,reftype,返回底层数据的类型,方式同ref.
    猜你喜欢
    • 1970-01-01
    • 2021-12-15
    • 2015-02-06
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多