如果第二个哈希中的键值对相同,我想比较哈希值。我不想使用smartmatch,因为它会发出警告。
将两个哈希与整数,字符串以及可能还有数组进行比较的最佳方法是什么?
use warnings;
use diagnostics;
my $hash1={"key_str"=>"a string", "key_int"=>4};
my $hash2={"key_str"=>"b string", "key_int"=>2};
foreach my $key ( keys(%$hash1) ) {
if ($hash1->{$key} != $hash2->{$key}) {
print($key);
}
}
预期的输出是:
Argument "b string" isn't numeric in numeric ne (!=) at hash_compare.pl line 8 (#1)
(W numeric) The indicated string was fed as an argument to an operator
that expected a numeric value instead. If you're fortunate the message
will identify which operator was so unfortunate.
Argument "a string" isn't numeric in numeric ne (!=) at hash_compare.pl line 8 (#1)
答案 0 :(得分:8)
首先,Perl没有类型。它不区分字符串和数字(在外面)。
此外,它在这个级别上的数字和字符串之间没有区别。 数字上下文和字符串上下文对于检查大于或小于的内容很重要。考虑一下:
my $foo = 200;
my $bar = 99;
print $foo > $bar ? $foo : $bar;
显然它会打印200
,因为200在数值上大于99。
my $foo = 200;
my $bar = 99;
print $foo gt $bar ? $foo : $bar;
但是这会打印99
,因为9
是字母数字(如字符串中)大于2
。它比较了字符的代码点数。
但如果你要做的只是检查不平等,那么ne
运算符就可以了。即使您不确定输入中是否还有数字以外的东西。
foreach my $key ( keys(%$hash1) ) {
if ($hash1->{$key} ne $hash2->{$key}) {
print($key);
}
}
eq
(和ne
)非常聪明,可以查看数字最初是字符串还是没有引号的数字,因为这些数字的内部表示不同。
警告,技术细节未来。
标量值保存在_SV_s中。这些术语可以包含不同的东西。对于称为 IV 的简单整数,还有一个特殊的内部类型,对于字符串,还有一个名为 PV 的内部类型。当你在字符串中使用数字时,Perl会根据需要在这两者之间进行内部转换,反之亦然。
您可以使用Devel::Peek中的Dump
获取有关数据内部表示的一些调试信息。
use Devel::Peek;
Dump("01");
Dump(01);
这将输出:
SV = PV(0x19560d0) at 0x19327d0
REFCNT = 1
FLAGS = (POK,READONLY,IsCOW,pPOK)
PV = 0x1c94fd0 "01"\0
CUR = 2
LEN = 10
COW_REFCNT = 0
SV = IV(0x19739b0) at 0x19739c0
REFCNT = 1
FLAGS = (IOK,READONLY,pIOK)
IV = 1
如您所见,第一个是字符串,第二个是数字。 但是,如果我们这样做
print "01" eq 01;
没有输出,因为01
是一个整数,并且会转换为"1"
进行比较。由于0
的{{1}}不等于"01"
,因此不会打印任何内容。
如果数据结构的值更复杂,则需要遍历结构。每种类型的元素都需要有自己的处理。可能有数组引用,哈希引用,标量引用,标量,glob引用,dualvars等。可能有一些你想特别对待的物品。
我建议看看Test::Deep如何实现这一点。如果您决定在生产代码中使用它(而不是单元测试),则可以使用Test::Deep::NoTest。
答案 1 :(得分:2)
您可以use Scalar::Util qw( looks_like_number );
确定该值是数字还是字符串。 Scalar::Util
是Perl附带的标准模块。有关标准模块的列表,请参阅perldoc perlmodlib
。
#!/usr/bin/env perl
# always use these two
use strict;
use warnings;
# handle errors in open and close
use autodie; # See http://perldoc.perl.org/autodie.html
use Scalar::Util qw( looks_like_number );
my $hash1={"key_str"=>"a string", "key_int"=>4};
my $hash2={"key_str"=>"b string", "key_int"=>2};
foreach my $key ( keys(%$hash1) ) {
if( looks_like_number( $hash1->{$key} ) && looks_like_number( $hash2->{$key} ) ){
if ($hash1->{$key} != $hash2->{$key}) {
print "number value of $key is different\n";
}
}else{
if ($hash1->{$key} ne $hash2->{$key}) {
print "string value of $key is different\n";
}
}
}
答案 2 :(得分:1)
我编写了一个不使用任何模块的程序。在很多情况下,我已在下面的程序中测试过,工作正常,但如果您发现任何失败的情况,请告诉我。
如果您不确定要比较的数据类型,请始终使用ne
进行比较。 !=
仅适用于整数,ne
适用于整数和字符串。
use strict;
use warnings;
use feature 'say';
my $hash1 = {
'key1' => 'value1',
'key2' => [1, 2, 2],
'key3' => {1=>1, 2=> [5, 7]},
};
my $hash2 = {
'key1' => 'value1',
'key2' => [1, 2, 2],
'key3' => {1=>1, 2=> [5, 7]},
};
my $is_same = 0;
$is_same = compare($hash1, $hash2);
if ($is_same) {
say "Same";
} else {
say "Not same";
}
sub compare {
my ($value1, $value2) = @_;
my $is_same = 1;
if (ref($value1) eq "ARRAY") {
if (is_same_sized_array($value1, $value2)) {
foreach (my $i = 0; $i < @$value1; $i++) {
if (ref $value1->[$i] eq ref $value2->[$i]) {
$is_same = compare($value1->[$i], $value2->[$i]);
return 0 unless $is_same;
} else {
return 0;
}
}
} else {
return 0;
}
} elsif (ref($value1) eq "HASH") {
if (is_same_sized_array([keys %$value1], [keys %$value2])) {
foreach my $key (sort keys %$value1) {
if (exists $value2->{$key} && ref $value1->{$key} eq ref $value2->{$key}) {
$is_same = compare($value1->{$key}, $value2->{$key});
return 0 unless $is_same;
} else {
return 0;
}
}
} else {
return 0;
}
} else {
if ($value1 ne $value2) {
return 0;
}
}
return $is_same;
}
sub is_same_sized_array {
my ($arr1, $arr2) = @_;
return (@$arr1 == @$arr2) || 0;
}