【问题标题】:How to expand a Hash of Arrays?如何扩展数组的哈希?
【发布时间】:2011-06-14 19:51:06
【问题描述】:

我不知道“扩展”是否是正确的词,但这是我想做的 =)

这个脚本

#!/usr/bin/perl

use warnings; 
use strict;

my %HoA = (
    group1 => [ "user1", "user2" ],
    group2 => [ "group1", "user3" ],
    group3 => [ "group1", "group2" ],
    group4 => [ "group3", "user2" ],
    );

foreach my $group ( keys %HoA ) {
    print "$group: @{ $HoA{$group} }\n"
}

输出

group1: user1 user2
group2: group1 user3
group3: group1 group2
group4: group3 user4

我想要的是用成员替换数组中的组。 IE。所以输出和$HoA变成了

group1: user1 user2
group2: user1 user2 user3
group3: user1 user2 user3
group4: user1 user2 user3 user4

也许“搜索和替换”和删除重复项会更好地解释我想要做什么?

【问题讨论】:

  • 如果你有循环引用怎么办?即如果 group1 有 group2,而 group2 有 group1 怎么办?
  • 那么我想发送那个组,并打印一个错误。

标签: arrays perl hash perl-data-structures


【解决方案1】:

假设您已提供数据,以下循环将使用扩展数组创建一个新哈希。此算法假定组将按排序顺序解析(group2 将仅依赖于 group1,group3 在 1 / 2 上,...)。

my %expanded;
for my $group (sort keys %HoA) {
    my %seen;
    $expanded{$group} = [
        grep {not $seen{$_}++}
        map {exists $expanded{$_} ? @{$expanded{$_}} : $_}
        @{$HoA{$group}}
    ];
    print "$group: @{ $expanded{$group} }\n"
}

哪个打印:

组 1:用户 1 用户 2 组 2:用户 1 用户 2 用户 3 组 3:用户 1 用户 2 用户 3 组 4:用户 1 用户 2 用户 3

如果你不能假设一个解决顺序,以下是有点蛮力,但应该工作:

my %HoA = (
    group1 => [ "user1", "user2" ],
    group2 => [ "group1", "user3" ],
    group3 => [ "group1", "group2" ],
    group4 => [ "user5", "group5" ],
    group5 => [ "group3", "user2" ],
    );

my @to_expand = keys %HoA;

my %final;
my $tries = @to_expand;
to_expand: while (@to_expand and $tries) {
    my $next = shift @to_expand;

    my (@users, @groups);
    for (@{ $HoA{$next} }) {
        if (/^group/) {
            push @groups, $_;
        } else {
            push @users, $_;
        }
    }
    for my $group (@groups) {
        if (exists $final{$group}) {
            push @users, @{$final{$group}}
        } else {
            $tries--;
            push @to_expand, $next;
            next to_expand;
        }
    }
    $tries++;
    my %seen;
    $final{$next} = [grep {not $seen{$_}++} @users];
}
if (@to_expand) {
    print "error with groups: @to_expand\n";
}

for (sort keys %final) {
    print "$_: @{$final{$_}}\n";
}

哪个打印:

组 1:用户 1 用户 2 组 2:用户 3 用户 1 用户 2 组 3:用户 1 用户 2 用户 3 组 4:用户 5 用户 2 用户 1 用户 3 组 5:用户 2 用户 1 用户 3

如果有错误(比如 group3 依赖于 group5),那么你会得到这个输出:

组错误:group4 group5 group3 组 1:用户 1 用户 2 组 2:用户 3 用户 1 用户 2

可能有更好的算法来解决这个问题。

【讨论】:

  • 遗憾的是,我不能做出这样的假设 =( 各组可以以任何组合相互包含。非常令人印象深刻的脚本!
  • for my $group (@groups) { 之前,我明白发生了什么。你能解释一下$tries 的用途吗? %seen 是如何工作的?
  • @Sandra => $tries 是一个计数器,类似于 leonbloy 的答案中的 $cont 和 Axeman 中的 $deep。当面临循环或其他无法解决的依赖关系时,它可以防止这些算法进入无限循环(或深度递归)。 %seen 背后的想法是保留已添加用户的 O(1) 查找表。为每个用户查询此表,如果未看到该用户,则将其添加到最终列表中。
【解决方案2】:

如果存在递归,这将引发错误——虽然不是万无一失,也不是很优雅

use strict;

my %HoA = (
    group1 => [ "user1", "user2" ],
    group2 => [ "group1", "user3" ],
    group3 => [ "group1", "group2" ],
    group4 => [ "group3", "user2" ],
    );

my %ex=(); # expanded hash

foreach my $g ( keys %HoA ) { # first population
     $ex{$g} = {};
     AddArrayToHash($ex{$g},$HoA{$g});
}
my $goon = 1;
my $cont =0;
while($goon) { # iterate
    $goon=0;
    die "too many iterations RECURSIVE DEFINITION?" if($cont++ >10) ;
    foreach my $g ( keys %ex ) {
        foreach my $u ( keys %{$ex{$g}} ) {
            if($ex{$u}) {
                delete $ex{$g}->{$u};
                AddArrayToHash($ex{$g},[ keys %{$ex{$u}}] );
                $goon = 1;
            }
        }
    }
}

foreach my $group ( sort keys %ex ) {
    print "$group: " . join(" ",sort  keys %{$ex{$group}}) ."\n";
}

sub AddArrayToHash {
    my($refhash,$refarray)=@_;
    foreach my $e (@$refarray) {
        $refhash->{$e} = 1;
    }
}

【讨论】:

    【解决方案3】:

    我会尝试类似的东西

    #!/usr/bin/perl
    
    use warnings; 
    use strict;
    
    my %HoA = (
        group1 => [ "user1", "user2" ],
        group2 => [ "group1", "user3" ],
        group3 => [ "group1", "group2" ],
        group4 => [ "group3", "user2" ],
        );
    
    
    my %users; 
    
    foreach my $group ( sort keys %HoA ) {
      %users = ();
    
      print "$group: ";
      print_key($group, $HoA{$group});
      print join " ", sort keys %users;
      print "\n";
    }
    
    sub print_key {
      my $group = shift;
    
      foreach my $item (@{$HoA{$group}}) {
        if (exists $HoA{$item}) {
           print_key($item);
        }
        else {
           $users{$item}++;
        }
      }
    }
    

    【讨论】:

      【解决方案4】:

      我不知道为什么人们不得不写这么多代码:

      sub expand_group { 
          my ( $ref, $arref, $deep ) = @_;
          croak 'Deep Recursion!' if ++$deep > scalar( keys %$ref );
          return map { 
              exists $ref->{$_} ? expand_group( $ref, $ref->{$_}, $deep ) : $_ 
          } @$arref
          ;
      }
      
      sub expand_groups { 
          my ( $block, $group_ref ) = @_;
          while ( my ( $key, $val ) = each %$group_ref ) {
              $block->( $key, expand_group( $group_ref, $val, 1 ));
          }
      }
      
      expand_groups( sub { say join( ' ', @_ ); }, \%HoA );
      

      【讨论】:

      • 没有必要 &% 原型,除非你想像 expand_groups { say join ' ', @_ } %HoA 这样称呼它。
      猜你喜欢
      • 1970-01-01
      • 2016-01-02
      • 2011-04-20
      • 1970-01-01
      • 2023-03-17
      • 2021-04-10
      • 2016-06-27
      • 2016-09-16
      • 2016-03-30
      相关资源
      最近更新 更多