【问题标题】:Perl - Recursive Function that Calls itself for unravelling hash of arrays whose elements might be keys in the same hashPerl - 递归函数,它调用自身来解开数组的散列,其元素可能是同一散列中的键
【发布时间】:2016-08-23 22:03:33
【问题描述】:

我想编写一个Perl 函数,该函数将读取具有特定嵌套格式的哈希并将其内容传输到具有不同格式的哈希中。输入哈希是数组的哈希,其中所有键和所有数组元素都是数字。数字代表系统发育树中的节点。整个散列中没有重复的数组元素(显然也没有任何重复的键),但一些数组元素对应于键;散列中的每个数字恰好出现一次或两次。这是一个例子:

my %hash_A = ( '1' => [2,7,9], 
               '2' => [3,5],
               '3' => [4], 
               '5' => [6],
               '7' => [8] );

所需的输出散列是数组的散列,其中散列中的每个数字都是指向自身的键,它指向的任何元素以及它的元素指向的任何元素。这是 %hash_A 的所需输出:

my %hash_B = ( '1' = [1,2,3,4,5,6,7,8,9],
               '2' = [2,3,4,5,6],
               '3' = [3,4],
               '4' = [4],
               '5' = [5,6],
               '6' = [6],
               '7' = [7,8],
               '8' = [8],
               '9' = [9] );

注意$hash_B{'2'} 包含:

  • 本身
  • 它在%hash_A中指向的3和5
  • 3 和 5 在%hash_A 中指向的 4 和 6

虽然%hash_B{'1'} 包含整个散列中的每个元素,因为它们是......我想称之为“子散列”的元素,但这在结构上似乎并不准确。

我开始编写一个子例程,它将循环通过 %hash_A 填充 %hash_B,但这是一个缓慢而令人困惑的过程,我什至无法判断我是否走在正确的轨道上。我想我需要以这样一种方式编写函数,它可以在循环中间调用自己,以跟踪每个 array_element => key => array_element => key => array_element 的踪迹到最后。这是我的子程序(不起作用):

sub one_level_deeper {
    my ($ref) = @_;
    my %hash_B;
    foreach my $k (keys %$ref) {
         push(@{$hash_B{$k}}, $k);
         my @temp_array = @$ref{$k};
         foreach ( @temp_array ) {
             if (exists $$ref{$k} ) {
                 my %temp_hash = &one_level_deeper($$ref{$k}); #HASH CALLS ITSELF
                 push(@{$hash_B{$k}}, $temp_hash{$k});
             }
             push(@{$hash_B{$k}}), $_;
         }
    }
return(%hash_B);
}

我希望得到有关如何通过修复我的子例程或通过 Perl 中的其他方法从 %hash_A 生成 %hash_B 的反馈。

【问题讨论】:

    标签: arrays perl hash


    【解决方案1】:

    跟进 Borodin 使用 Graph 的建议:

    use Graph::Directed;
    my $graph = Graph::Directed->new;
    
    # Build a graph from the input data
    for my $source (keys %hash_A) {
      for my $dest (@{ $hash_A{$source} }) {
        $graph->add_edge($source => $dest);
      }
    }
    
    # For each vertex retrieve its descendants (plus itself).
    my %hash_B;
    for my $vertex ($graph->vertices) {
      $hash_B{$vertex} = [ 
        sort { $a <=> $b } 
        $vertex, $graph->all_successors($vertex) 
      ];
    }
    

    sort 使输出与您的问题相匹配,但如果每个输出数组中值的顺序对您来说实际上并不重要,您可以忽略它。

    【讨论】:

      【解决方案2】:

      我认为如果你解释一下你的数据中的数字意味着什么可能会更清楚

      你所拥有的是一个有向图。原始数据仅代表每个节点的子节点,而您想要的结果列出所有 后代

      您必须构建一个包含所有已知信息的数据结构,并允许您查询它关于两个节点之间的关系

      我认为 CPAN Graph 模块套件可能会满足您的所有需求。不幸的是,我目前正在使用我的平板电脑,所以我无法编写示例代码。不过应该是直截了当的,有空我早上查这个问题



      更新

      这是我想要的代码。在图中,所有数据点称为edges,所有关系称为edges。该程序通过分配来自%hash_A 的所有边来简单地构建图,然后使用all_successors 方法读回每个顶点的后代列表。通常一个顶点不被认为是它自己的继承者之一,但你似乎想要在你的情况下,所以我已经明确地将每个顶点添加到它自己的@descendants 列表中。结果列表被插入到每个顶点的%hash_B

      我已经打印出正在构建%hash_B 的值,并且还使用Data::Dump 显示了结果。它与您显示的所需输出相同

      use strict;
      use warnings 'all';
      
      use Graph;
      
      my %hash_A = (
          '1' => [2,7,9], 
          '2' => [3,5],
          '3' => [4], 
          '5' => [6],
          '7' => [8],
      );
      
      my $graph = Graph->new(directed => 1);
      
      while ( my ($from, $to) = each %hash_A ) {
          $graph->add_edge($from, $_) for @$to;
      }
      
      my %hash_B;
      
      for my $from ( sort { $a <=> $b } $graph->vertices ) {
          my @descendants = sort { $a <=> $b } ($from, $graph->all_successors($from));
          printf "%d -> %s\n", $from, join ', ', @descendants;
          $hash_B{$from} = \@descendants if @descendants;
      }
      print "\n";
      
      use Data::Dump;
      dd \%hash_B;
      

      输出

      1 -> 1, 2, 3, 4, 5, 6, 7, 8, 9
      2 -> 2, 3, 4, 5, 6
      3 -> 3, 4
      4 -> 4
      5 -> 5, 6
      6 -> 6
      7 -> 7, 8
      8 -> 8
      9 -> 9
      
      {
        1 => [1 .. 9],
        2 => [2 .. 6],
        3 => [3, 4],
        4 => [4],
        5 => [5, 6],
        6 => [6],
        7 => [7, 8],
        8 => [8],
        9 => [9],
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-30
        • 2010-12-23
        • 2014-05-19
        • 2012-08-03
        • 2013-02-05
        • 1970-01-01
        • 2015-07-01
        • 1970-01-01
        相关资源
        最近更新 更多