问题
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