使用perl按依赖性排序数组

时间:2012-08-28 19:40:53

标签: algorithm perl graph sorting cpan

有一系列哈希值,

my @arr = get_from_somewhere();

@arr内容(例如)是:

@arr = (
  { id => "id2",    requires => 'someid', text => "another text2" },
  { id => "xid4",   requires => 'id2',    text => "text44" },
  { id => "someid", requires => undef,    text => "some text" },
  { id => "id2",    requires => 'someid', text => "another text2" },
  { id => "aid",    requires => undef,    text => "alone text" },
  { id => "id2",    requires => 'someid', text => "another text2" },
  { id => "xid3",   requires => 'id2',    text => "text33" },
);

需要类似的东西:

my $texts = join("\n",  get_ordered_texts(@arr) );

soo需要写一个sub,从哈希中返回text s的数组, - 依赖顺序,所以从上面的例子中得到:

"some text",     #someid the id2 depends on it - so need be before id2
"another text2", #id2    the xid3 and xid4 depends on it - and it is depends on someid
"text44",        #xid4   the xid4 and xid3 can be in any order, because nothing depend on them
"text33",        #xid3   but need be bellow id2
"alone text",    #aid    nothing depends on aid and hasn't any dependencies, so this line can be anywhere

正如你所看到的,在@arr中可能有一些重复的“行”,(上例中的“id2”),只需要输出任何id。

尚未提供任何代码示例,因为我们不知道如何开始。 ( 存在一些可用于解决方案的CPAN模块?

有人能指出我正确的方向吗?

4 个答案:

答案 0 :(得分:13)

使用Graph

use Graph qw( );

my @recs = (
   { id => "id2",    requires => 'someid', text => "another text2" },
   { id => "xid4",   requires => 'id2',    text => "text44" },
   { id => "someid", requires => undef,    text => "some text" },
   { id => "id2",    requires => 'someid', text => "another text2" },
   { id => "aid",    requires => undef,    text => "alone text" },
   { id => "id2",    requires => 'someid', text => "another text2" },
   { id => "xid3",   requires => 'id2',    text => "text33" },
);

sub get_ordered_recs {
   my %recs;
   my $graph = Graph->new();
   for my $rec (@_) {
      my ($id, $requires) = @{$rec}{qw( id requires )};

      $graph->add_vertex($id);
      $graph->add_edge($requires, $id) if $requires;

      $recs{$id} = $rec;
   }

   return map $recs{$_}, $graph->topological_sort();
}

my @texts = map $_->{text}, get_ordered_recs(@recs);

答案 1 :(得分:4)

一个有趣的问题。

这是我的第一轮解决方案:

sub get_ordered_texts {
    my %dep_found;  # track the set of known dependencies
    my @sorted_arr; # output

    my $last_count = scalar @_; # infinite loop protection
    while (@_ > 0) {
        for my $value (@_) {

            # next unless we are ready for this text
            next if defined $value->{requires}
                and not $dep_found{ $value->{requires} };

            # Add to the sorted list
            push @sorted_arr, $value->{text};

            # Remember that we found it
            $dep_found{ $value->{id} }++;
        }

        if (scalar @_ == $last_count) die "some requirements don't exist or there is a dependency loop";
        $last_count = scalar @_;
    }

    return \@sorted_arr;
}

这不是非常有效,可能会在O(n log n)时间内运行,但如果你没有庞大的数据集,那可能就行了。

答案 2 :(得分:2)

我会使用有向图来表示依赖树,然后走图。我使用Graph.pm

做了类似的事情

每个哈希值都是一个图形顶点,边缘代表依赖关系。这有额外的好处,支持将来更复杂的依赖关系,以及提供使用图形的快捷功能。

答案 3 :(得分:1)

  1. 你没有说依赖关系的做法是彼此“独立”的。

    E.g。 id1需要id2; id3需要id4; id3需要id5。订单应该是什么? (4/5之前的2和3之前的1除外)

  2. 你想要的基本上是依赖关系树(有向图)的BFS(广度优先搜索)(或森林取决于#1的答案 - 森林是一组非连接树)。

    要做到这一点:

    • 查找所有根节点(自身没有需求的ID)

      您可以通过在数据结构上使用grep对所有ID进行哈希来轻松完成此操作

    • 将所有这些根模式放入起始数组中。

    • 然后实施BFS。如果您需要帮助在Perl中使用数组和循环实现基本BFS,请提出单独的问题。可能有一个CPAN模块,但算法/代码相当简单(至少一次你写了一次:)