【问题标题】:Perl: Access hash of dynamic depthPerl:访问动态深度的哈希
【发布时间】:2016-04-20 18:27:21
【问题描述】:

我正在努力访问/修改未知(即动态)深度的哈希。

假设我正在从文件中读取测量值表(长度、宽度、高度),然后计算面积和体积以创建如下所示的哈希:

#                       #Length  Width  Height  Results
my %results = (     
                        '2' => {        
                                '3' => {        
                                        '7' => {
                                                'Area' => 6,
                                                'Volume' => 42,
                                                },
                                        },
                                },
                        '6' => {        
                                '4' => {        
                                        '2' => {
                                                'Area' => 24,
                                                'Volume' => 48,
                                                },
                                        },
                                },
                        );

我了解如何访问哈希中的单个项目,例如$results{2}{3}{7}{'Area'} 会给我6,或者我可以使用exists $results{2}{3}{7}{'Area'} 检查是否在输入文件中找到了该测量组合。但是,{} 大括号系列的符号假设我在编写代码时知道将有 4 层键。

如果有更多或更少,而我只是在运行时才发现呢?例如。如果文件中只有长度和宽度,你将如何编写代码来访问像$results{2}{3}{'Area'} 这样的哈希?

即给定一个哈希和动态长度的嵌套键列表,在该哈希中可能有也可能没有结果条目,您如何访问哈希以进行基本操作,例如检查该键组合是否具有值或修改该值?

我几乎想要这样的符号:

my @hashkeys = (2,3,7);

if exists ( $hash{join("->",@hashkeys)} ){
    print "Found it!\n";
}

我知道您可以访问散列的子散列并获取它们的引用,因此在最后一个示例中,我可以遍历 @hashkeys,检查每个散列是否在该键处具有子散列,如果有,为下一次迭代保存对该子哈希的引用。然而,这感觉很复杂,我怀疑已经有一种方法可以更容易地做到这一点。

希望这足以理解我的问题,但如果没有,我可以尝试制定 MWE。

谢谢。

【问题讨论】:

  • 这似乎不是一个很好的数据结构。一方面,你不能有两个具有相同维度的项目,因为哈希键是唯一的。使用带有 lengthwidthheight 键的平面结构会更有意义。
  • 嗨,我不确定这是一个限制。这只是我正在为工作做的更复杂的事情的简化示例,但即使在这里,当我遇到一组给定的测量值时,我也可以直接用它们索引散列以找出区域等。如果我两次查看相同的测量值,我不需要重新计算面积等。这就是为什么我使用嵌套散列,每个级别的键都是单独的测量值。
  • 这是真实数据吗?或者你有没有给占位符数据,或者是作业?作为@ThisSuitIsBlackNot says above,我不明白你为什么首先组织这样的事情。如您所见,这是有问题的。听起来你只是有一个 things 的列表,其中包含长度、宽度、高度、面积和体积,其中一些可能会丢失,我会像这样构造它:作为一个由五个组成的数组-项目数组。如果取决于您的应用程序如何更快地访问特定条目
  • 如果您确定这是您想要做的——并且考虑到您告诉我们的内容,那么不应该这样做——您可以查看Data::Diver。但看起来你是从一个糟糕的数据设计开始,并试图将其挤入可用性。你的数据的形状是你的要求——没有必要坚持那些不能完全做你需要的东西
  • “如果我看到两次相同的测量值,我不需要重新计算面积等。”也许你应该看看Memoize

标签: perl hash hash-of-hashes


【解决方案1】:

所以这里有一个递归函数,它或多或少地做了你想要的:

sub fetch {
    my $ref = shift;
    my $key = shift;
    my @remaining_path = @_;

    return undef unless ref $ref;
    return undef unless defined $ref->{$key};
    return $ref->{$key} unless scalar @remaining_path;
    return fetch($ref->{$key}, @remaining_path);
}

fetch(\%results, 2, 3, 7, 'Volume');  # 42
fetch(\%results, 2, 3);               # hashref
fetch(\%results, 2, 3, 7, 'Area', 8); # undef
fetch(\%results, 2, 3, 8, 'Area');    # undef

但是请检查其他人已经给出的关于不良数据结构的评论,因为这是非常真实的。如果你仍然认为这是你需要的,至少使用 for 循环重写它,因为 perl 不会优化尾递归。

【讨论】:

  • 我将其标记为 asnwer,因为它确实回答了我的问题,但对于其他考虑类似方法的人,请先查看我的问题下方的 cmets。我最终改变了我的方法,因为如上所述,从一开始就没有考虑好。谢谢。
【解决方案2】:

看看“man perlvar”中的$;

http://perldoc.perl.org/perlvar.html#%24%3b

您可以使用这个想法将可变长度数组转换为单个键。

my %foo;
my (@KEYS)=(2,3,7);
$foo{ join( $; , @KEYS ) }{Area}=6;
$foo{ join( $; , @KEYS ) }{Volume}=42;

【讨论】:

  • $; 的意义在于它会神奇地将$foo{@keys} 变成$foo{join $;, @keys }。如果您要手动执行此操作,您不妨使用更方便且更适合数据的分隔符,例如,
猜你喜欢
  • 2013-06-06
  • 2019-07-22
  • 2014-05-06
  • 2013-04-16
  • 2013-12-30
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
相关资源
最近更新 更多