Заказ массива по зависимостям с perl
Имеет массив хэшей,
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
из хэшей, - в зависимом порядке, поэтому из приведенного выше примера нужно получить:
"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" в приведенном выше примере), нужно выводить только один раз на любой идентификатор.
Пока не указывается какой-либо пример кода, потому что у вас нет идеи, как начать.; (
Существует какой-то модуль CPAN, который можно использовать для решения?
Может ли кто-нибудь указать мне в правильном направлении?
Ответы
Ответ 1
Использование 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);
Ответ 2
Интересная проблема.
Здесь мое решение первого раунда:
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)
время или что-то в этом роде, но если у вас нет огромного набора данных, возможно, это нормально.
Ответ 3
Я бы использовал ориентированный граф, чтобы представить дерево зависимостей, а затем ходить по графику. Я сделал что-то очень похожее, используя Graph.pm
Каждый из ваших хэшей будет вершиной графика, а край будет представлять зависимость. Это имеет дополнительное преимущество для поддержки более сложных зависимостей в будущем, а также предоставления функций быстрого доступа для работы с графиком.
Ответ 4
-
вы не сказали, что делать с зависимостями "независимы" друг от друга.
например. id1 требует id2; id3 требует id4; id3 требует id5. Каким должен быть порядок? (кроме 1 до 2 и 3 перед обоими 4/5)
-
В основном вы хотите, чтобы BFS (Breadth First Search) дерева (ориентированный граф) зависимостей (или леса в зависимости от ответов на # 1) - лес, являющийся набором несвязанных деревьев).
Для этого:
-
Найти все корневые узлы (ids, которые сами не имеют требования)
Вы можете легко сделать это, сделав хэш ВСЕХ идентификаторов, используя grep
в вашей структуре данных
-
Поместите все эти корневые режимы в исходный массив.
-
Затем реализуем BFS. Если вам нужна помощь по внедрению базовой BFS с использованием массива и цикла в Perl, задайте отдельный вопрос. Может быть модуль CPAN, но алгоритм/код довольно тривиальный (по крайней мере, как только вы написали его один раз:)