Perl - Hash::Merge 丢失密钥

Perl - Hash::Merge loses keys

我正在尝试在 Perl 中合并两个 blessed 哈希。

我是运行以下代码:

#!usr/bin/perl
use strict;
use warnings;
use Hash::Merge;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;

my $hash1 = bless( {
                 'CalcPorts' => {
                                  'helper_1' => {
                                                  'Scope' => [
                                                               ''
                                                             ],
  
                                                },
                                  'helper_2' => {
                                                  'Scope' => [
                                                               ''
                                                             ],
 
                                                },
                                },
 
               }, 'IB' );
my $hash2 = bless( {
                 'CalcPorts' => {
                                  'helper_2' => {
                                                  'Scope' => [
                                                               'd'
                                                             ],
 
                                                },
                                },
 
               }, 'IB' );

my $merger = Hash::Merge->new('LEFT_PRECEDENT');    
my $hash3 = $merger->merge($hash2, $hash1);

print Dumper($hash3);

输出是这样的:

$VAR1 = bless( {
                 'CalcPorts' => {
                                  'helper_2' => {
                                                  'Scope' => [
                                                               'd'
                                                             ]
                                                }
                                }
               }, 'IB' );

尽管我希望“helper_1”会在那里... 任何想法我做错了什么? 感谢您的帮助:)

Hash::Merge 认为任何 ref 不是 HASHARRAY 的东西都是标量,并对这些项目应用标量合并规则(参见 those lines 的执行 Hash::Merge)。合并 2 个标量时,Hash::Merge 要么丢弃其中一个,要么创建一个数组来存储它们。 None 这个选项合并了受祝福的哈希值。

为了克服这个问题,你可以先取消祝福你的哈希(例如使用Data::Structure::Util::unbless),然后合并它们,然后重新祝福它们:

use Data::Structure::Util qw(unbless);
my $class = ref $hash1;
unbless $hash1;
unbless $hash2;
my $hash3 = bless $merger->merge($hash2, $hash1), $class;

如果你的主哈希中有 blessed 哈希,那么你可以使用 add_behavior_spec 方法定义你自己的 Hash::Merge 行为:对于 SCALAR-SCALAR 情况,检查两个标量是否都是受祝福的引用,如果是,则取消祝福、合并和重新祝福:

$merger->add_behavior_spec(
    { 'SCALAR' => {
        'SCALAR' => sub {
            my $self  = &Hash::Merge::_get_obj;
            my ($left, $right) = @_;
            my ($class_left, $class_right) = (ref $left, ref $right);
            if ($class_left && $class_left eq $class_right) {
                unbless $left;
                unbless $right;
                return bless $self->merge($left, $right), $class_left;
            } else {
                return $_[1]; # Or something else
            }
        },
        'ARRAY'  => ...,
        'HASH'   => ...,
    },
    ARRAY => { ... },
    HASH  => { ... }

为简洁起见,我将 ... 留给不相关的案例。您可以从 the source of Hash::Merger (choosing the behavior that you want). Or, maybe easier, you can use get_behavior_spec and get_behavior 方法中复制粘贴这些内容以更改当前行为的 SCALAR-SCALAR 情况:

my $behavior = $merger->get_behavior_spec($merger->get_behavior);
my $old_behavior_scalar_scalar = $behavior->{SCALAR}{SCALAR};
$behavior->{SCALAR}{SCALAR} = sub {
    my $self  = &Hash::Merge::_get_obj;
    my ($left, $right) = @_;
    my ($class_left, $class_right) = (ref $left, ref $right);
    if ($class_left && $class_left eq $class_right) {
        unbless $left;
        unbless $right;
        return bless $self->merge($left, $right), $class_left;
    } else {
        # Regular scalars, use old behavior
        return $old_behavior_scalar_scalar->($left, $right);
    }
};

请注意,这不能很好地处理您想要合并 2 个散列到 2 个不同包的情况。你必须为那些实施特殊情况。 (不清楚合并此类哈希的一般规则是什么)