2011-11-08 55 views
3

考虑以下环比:在不使用键()的情况下访问Perl 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循环之前将密钥复制到一个数组或哈希值(这是我如何开始的),但我希望避免开销。

+0

如果您要复制密钥列表,则不再需要使用(不幸的是)危险的'each%$ h',而是可以执行foreach循环。这个散列有多大? – Schwern

+0

@Schwern:大约24,000个键(所有嵌套散列的总和)。它包含Sybase数据库的所有特权分配。我意识到我可以避免使用'each',或者复制密钥,或者找到其他的实现。只是想我会提出这个问题,看看是否有可能。 – MisterEd

+0

我认为可以有效地做到这一点,但需要改变接口以及XS代码,以在每次迭代后保留哈希迭代器以防重置。 perl5i的'each()'方法有这样一个接口,但它不能抵抗你的情况。它想。 https://github.com/schwern/perl5i/issues/210 – Schwern

回答

2

编号each使用散列的迭代器,并且不能在不使用其迭代器的情况下迭代散列,即使在C API中也是如此。 (这意味着聪明的比赛无论如何都无济于事。)

由于每个哈希都有其自己的迭代器,因此您必须在使用each进行迭代的相同哈希上调用keys来解决此问题。既然你没有问题在那个哈希上调用keys,你可以简单地使用keys而不是each?或者可能调用keys一次,存储结果,然后迭代存储的密钥?

+0

谢谢,这是我正在寻找的权威性的肯定或否定的答案,我会回去放置像你和@cjm建议的哈希键(和我一样在做)。 – MisterEd

3

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

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

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

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

+0

谢谢,我就是这样开始的,但是我删除了额外的散列,因为我已经有了散列中的数据,这看起来很浪费;更不用说你提到的另一个结构要更新的问题。 – MisterEd

0

你是否试图做这个没有任何while循环?你可以通过引用它的测试存在与否在哈希,不产生错误

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

} 
+0

他并不试图确定_current_ hashref是否具有该键。他试图确定'$ h'中的hashrefs是否有这个键。 – cjm

+0

你不喜欢perl吗? my $ yes; (定义$ h - > {$ _} - > {1}){foreach(sort {defined $ h - > {$ b} - > {1}?1:0} keys%$ h){$ yes = 1 ; last;} – ryansstack

+0

但是键的使用重置迭代器 – ysth

1

你几乎肯定会发现,聚集在第二级哈希的“开销”低于任何其他解决方案。每次你想做检查时,简单的哈希查找比迭代整个数据结构要快得多。

-1

为什么不在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

+0

@dolmen - 谷歌“XY问题”。通常没有“Perl问题”。最好的工具来解决“。Sybase似乎是基于OP的评论的可用工具之一 – DVK