【问题标题】:Access nested hash in Perl HoH without using keys()?在不使用键()的情况下访问 Perl HoH 中的嵌套哈希?
【发布时间】:2011-11-08 23:29:51
【问题描述】:

考虑以下 HoH:

$h = {
    a => {
           1 => x
    },
    b => {
           2 => y
    },
    ...
}

有没有办法在不调用keys(%$h) 的情况下检查第二个嵌套级别是否存在哈希键?例如,我想说这样的话:

if ( exists($h->{*}->{1}) ) { ...

(我知道你不能使用 * 作为哈希键通配符,但你明白了......)

我试图避免使用keys(),因为它会重置哈希迭代器,并且我正在循环使用$h

while ( (my ($key, $value) = each %$h) ) {
    ...
}

我能找到的最接近的语言结构是smart match operator (~~) mentioned here(并且在 perlref perldoc 中没有提及),但即使 ~~ 在 Perl 版本中可用,我也只能使用 (5.8.4),据我所知,在这种情况下它是行不通的。

如果无法完成,我想我会在进入 while 循环之前将密钥复制到数组或散列中(这是我开始的方式),但我希望避免开销。

【问题讨论】:

  • 如果您要复制键列表,则不再需要使用(不幸的是危险)each %$h,而是可以执行 foreach 循环。这个哈希有多大?
  • @Schwern:大约 24,000 个键(所有嵌套哈希的总和)。它包含 Sybase 数据库的所有权限分配。我意识到我可以避免each,或者复制密钥,或者找到其他一些实现。只是想我会提出这个问题,看看是否有可能。
  • 我认为可以有效地做到这一点,但需要更改接口以及 XS 代码以在每次迭代后保留哈希迭代器,以防它被重置。 perl5i 的each() 方法有这样一个接口,但它并不能防御你的情况。它愿意。 github.com/schwern/perl5i/issues/210

标签: perl hash-of-hashes


【解决方案1】:

不是真的。如果你需要这样做,我想我会创建一个合并哈希,列出所有二级键(在开始你的主循环之前):

my $h = {
    a => {
           1 => 'x'
    },
    b => {
           2 => 'y'
    },
};

my %all = map { %$_ } values %$h;

然后你的exists($h->{*}->{1}) 变成exists($all{1})。当然,如果您正在修改循环内的二级散列,这将不起作用(除非您适当地更新 %all)。该代码还假设$h 中的所有值都是hashrefs,但如果需要,这很容易修复。

【讨论】:

  • 谢谢,我就是这样开始的,但是我删除了额外的哈希,因为我已经将数据放在了哈希中,这似乎很浪费;更不用说你提到的如果事情发生变化需要更新另一个结构的问题。
【解决方案2】:

没有。 each 使用散列的迭代器,如果不使用它的迭代器,你就不能迭代散列,甚至在 C API 中也不行。 (这意味着无论如何智能匹配都无济于事。)

由于每个哈希都有自己的迭代器,因此您必须在已使用 each 进行迭代的同一哈希上调用 keys 才能遇到此问题。既然您在该哈希上调用keys 没有问题,您是否可以简单地使用keys 而不是each?或者也许调用一次keys,存储结果,然后遍历存储的键?

【讨论】:

  • 谢谢,这是我一直在寻找的明确的是或否的答案。我将回到像你和@cjm 建议的那样将密钥放入哈希中(就像我一开始所做的那样)。
【解决方案3】:

您几乎肯定会发现聚合二级哈希的“开销”低于任何其他解决方案。每次进行检查时,简单的哈希查找比遍历整个数据结构要快得多。

【讨论】:

    【解决方案4】:

    您是否尝试在没有任何 while 循环的情况下执行此操作?您可以通过引用它来测试它是否存在于哈希中,而不会产生错误

    while (  my ($key, $value) = each %{$h} ) {
        if ($value->{1}) { .. } 
    
    }
    

    【讨论】:

    • 他并没有试图确定 current hashref 是否有那个键。他正在尝试确定$h 中的任何 个hashrefs 是否具有该密钥。
    • 你不喜欢 perl 吗?我的$是的; foreach (sort { defined $h->{$b}->{1} ? 1:0 } keys %$h) { $yes = 1 if (defined $h->{$_}->{1};最后;}
    • 但是使用键会重置迭代器
    【解决方案5】:

    为什么不在 Sybase 本身而不是 Perl 中这样做呢?

    您正在尝试执行集合操作,这正是 Sybase 的初衷。

    假设您从“key1”、“key2”、“valye”列为“select *”的表中检索数据,只需执行以下操作:

    -- Make sure mytable has index on key1
    SELECT key1
    FRIN mytable t1
    WHERE NOT EXISTS (
        SELECT 1 FROM mytable t2
        WHERE t1.key1=t2.key1
        AND t2.key2 = 1
    )
    
    -----------
    -- OR
    -----------
    
    SELECT DISTINCT key1
    INTO   #t
    FROM   mytable
    
    CREATE INDEX idx1_t on #t (key1)
    
    DELETE #t
    FROM   mytable
    WHERE #t.key1=mytable.key1
    AND   mytable.key2 = 1
    
    SELECT key1 from #t    
    

    任何一个查询都返回没有 key2 of 1 的 1 级键的列表

    【讨论】:

    • 因为问题是关于 Perl 的。 Sybase 完全无关紧要。
    • @dolmen - 谷歌“XY 问题”。通常没有“Perl 问题”。有“最好的工具要解决的业务问题”。 Sybase 似乎是基于 OP 的 cmets 的可用工具之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-08
    • 2011-02-14
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多