2011-01-24 4 views
3

я не знаю, если «развернуть» это правильное слово, но вот то, что я хотел бы сделать =)Как расширить хэш массивов?

Этот скрипт

#!/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 

Я хотелось бы заменить группы в массиве членами. То есть поэтому выход и $HoA становится

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

Возможно, «найти и заменить» и удалить дубликаты будет лучшее объяснение того, что я хотел бы сделать?

+2

Что делать, если у вас есть круглые ссылки? то есть, если group1 имеет группу2, а group2 имеет группу 1? – CanSpice

+0

Затем я хотел бы отправить эту группу и напечатать сообщение об ошибке. –

ответ

3

Предполагая данные, которые вы указали, следующий цикл создаст новый хеш с расширенными массивами. Этот алгоритм предполагает, что группы будут разрешаться в отсортированном порядке (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" 
} 

который печатает:

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

Если вы не можете взять на себя порядок разрешения, следующее немного перебор, но должно работать:

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"; 
} 

который печатает:

 
group1: user1 user2 
group2: user3 user1 user2 
group3: user1 user2 user3 
group4: user5 user2 user1 user3 
group5: user2 user1 user3 

если есть ошибка или (скажем, group3 зависит от group5), то вы получите этот выход:

 
error with groups: group4 group5 group3 
group1: user1 user2 
group2: user3 user1 user2 

Там, наверное, лучше алгоритм там для этого.

+0

Я, к сожалению, не могу сделать это предположение = (Группы могут включать друг друга в любую комбинацию. Очень впечатляющий скрипт, тем не менее! –

+1

@Sandra => см. Обновление, которое должно охватывать ваш прецедент –

+0

Я понимаю, что происходит до ' для my $ group (@groups) {'. Можете ли вы объяснить, для чего используется' $ try'? И как работает '% seen'? –

3

Это будет сгенерировано сообщением об ошибке, если есть рекурсии - не Защищенные и не веры элегантная, хотя

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)[email protected]_; 
    foreach my $e (@$refarray) { 
     $refhash->{$e} = 1; 
    } 
} 
2

Я не знаю, почему люди должны были писать так много коды:

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); 
+0

Нет необходимости в прототипе' &% ', если вы не хотите называть его 'expand_groups { скажем, join '', @_}% HoA'. – friedo

2

Я бы попробовал что-то вроде

#!/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}++; 
    } 
    } 
} 
Смежные вопросы