Detecting element types in a mixed array

本秂侑毒 提交于 2019-12-10 13:09:31

问题


Im working with some code that has a subroutine which includes an array reference as one of the parameters. The elements in this incoming array can be either small arrays or strings.

I want to determine what type each element is in order to do something specific (i.e. if the element is an array, drill into it further via indexing, if the element is a string, use the string)

I have tried using the ref function to interrogate each array element. It seems to work for elements that are ARRAYs, but if the element is a string, I was expecting the ref to return SCALAR. However ref() seems to return nothing. What am I doing wrong? I would think ref() would return something.

Here is some sample code:

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

The output looks something like this:

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: 

回答1:


The ref returns an empty string if its argument isn't a reference. Docs say

Returns a non-empty string if EXPR is a reference, the empty string otherwise. The value returned depends on the type of thing the reference is a reference to.

The list that follows, which includes SCALAR, are the types that the reference can be to.

So when it has a string it returns an empty string, which evaluates to false. If you were to know for fact that it's ARRAY or string, you could do

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

Better check for the string (false) specifically, as you do, so to be able to detect any other types

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



回答2:


All of this is documented in perldoc perlfunc under ref

ref returns a false value if its parameter isn't a reference. It will return SCALAR only if the parameter is a reference to a scalar

You may also need to know that for a reference to blessed data—a Perl object—ref returns the class that the data has been blessed into, and not the underlying data type. If you need to distinguish between the two, then the core Scalar::Util module provides blessed, which returns the class into which the data has been blessed, and reftype, which returns the type of the underlying data in the same way as ref

You could make your foobar recursive to process an indefinitely-nested data structure, like this

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

output

  string1
    ele1_arraystr1
    ele1_arraystr2
  string2
    ele4_arraystr1
    ele4_arraystr2
  etc
    etc1
    etc2

Alternatively, if your array always consists of alternating strings and array references, you may find it easier to just assign it to a hash. That would use the strings as hash keys with their corresponding array references as hash values

This code shows the idea. I have used Data::Dump to reveal the resulting data structure

my %data = @array;

use Data::Dump;
dd \%data;

output

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


来源:https://stackoverflow.com/questions/39443078/detecting-element-types-in-a-mixed-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!