【问题标题】:Compare values of two hashes with mixed types比较两个混合类型的哈希值
【发布时间】:2018-04-27 07:04:10
【问题描述】:

如果第二个散列中的键值对相同​​,我想比较散列。我不想使用 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)

【问题讨论】:

    标签: perl hash


    【解决方案1】:

    首先,Perl 没有类型。它不区分字符串和数字(​​在外面)。

    此外,在此级别上,数字和字符串之间没有区别。 numerical contextstring context 如果您检查大于或小于什么,则很重要。考虑一下:

    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 会根据需要在这两者之间进行内部转换,反之亦然。

    您可以使用DumpDevel::Peek 获取有关数据内部表示的一些调试信息。

    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" 进行比较。由于"01"0 不等于1,因此不会打印任何内容。


    如果你的数据结构的值比较复杂,你需要遍历结构。每种类型的元素都需要有自己的处理方式。可能有数组引用、哈希引用、标量引用、标量、全局引用、dualvars 等等。可能有您想要特别对待的对象。

    我建议看看Test::Deep 是如何实现这一点的。如果您决定在生产代码(而不是单元测试)中使用它,您可以使用Test::Deep::NoTest

    【讨论】:

    • 很大程度上取决于您是否希望 "1""01" 等比较相等或不相等。
    • @Borodin 这是真的。但是eq 会处理这个问题,因为它有内部表示。我认为它知道"1" 在内部是一个SV,而1 是一个IV。请参阅此示例:say 01 eq 1; say 01 eq "1"; say "01" eq "1"; say "01" eq 1;
    • 是的,但我的意思是,OP 可能(或可能不)希望 "1""01" 匹配 "1000E-3",实际上是 "1000E-3"。有两种行为,在 dualvar 的情况下,结果可能不同。
    • @Borodin 在这种情况下,他们必须坚持我的第二点并自己构建很多东西。有来自 Scalar::Util 的 is_dual 来识别 _dualvar_s。有趣的是,Test::Deep 并没有这样做。 cmp_deeply(dualvar(1337, 'bar'), dualvar(1337, 'foo')) 将失败,因为 foo 不是 bar。如果数字不同但字符串相同,它将通过。它从不看数字部分。可能是因为 Test::Deep 在非引用上也使用了eq
    • @Borodin 我刚刚检查了 Test::Deep 的未解决问题。那里没有提到双变量。你在现实生活中遇到过这种情况吗?
    【解决方案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";
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我编写了一个不使用任何模块的程序。我已经在很多情况下测试了下面的程序,运行良好,但是如果您发现任何失败的情况,请告诉我。

      如果您不确定要比较的数据类型,请始终使用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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-09
        相关资源
        最近更新 更多