in reply to Re: Partial sort for dependency graph
in thread Partial sort for dependency graph

OK, I'm more awake now. And I realized that this wouldn't work.

Suppose that we have 4 objects, the second one depends on the fourth, and there are no other dependencies. Then Perl will (does a quick test) compare the first to the second, the third to the fourth, the first to the third and the third to the second. Then conclude that they are all equivalent and will return the list in the original order.

Note that the second and fourth never got compared at all.

The moral of the story is that sort algorithms assume that comparison functions are well-behaved. Your comparison function isn't so it misbehaves.

  • Comment on Re^2: Partial sort for dependency graph

Replies are listed 'Best First'.
Re^3: Partial sort for dependency graph
by jbert (Priest) on Nov 12, 2006 at 17:30 UTC
    Ah, thanks for this. You're absolutely right.

    The fundamental problem (as noted in other threads) is that we only have a partial ordering on the set, i.e. some pairs of nodes don't satisfy (a <= b or a => b). My sort comparator returns equal for such pairs (e.g. two root nodes) which breaks the sorting algo, as you noted.

    perldoc -f sort says: "The comparison function is required to behave. If it returns inconsistent results (sometimes saying $x1 is less than $x2 and sometimes saying the opposite, for example) the results are not well-defined." My comparator was badly-behaved for different reason. It doesn't satisfy transitivity: i.e. a == b && b == c does not imply a == c.

    As well as CPAN's Graph module, there is slightly more direct Sort::Topological which appears to do the job (in essentially the same way the other code posted appears to do it - associate a depth and sort on that).

    (Side note: if testing other approaches, note that your example list is already sorted - the first three elts are all root nodes). In the code below, I've reversed the dependency, so that the fourth depends on the second.)

    #!/usr/bin/perl use strict; use warnings; use Sort::Topological qw/toposort/; use Class::Struct Item => { id => '$', deps => '@', }; my @items; push @items, Item->new(id => $_) for 1..4; # Make $items[1] a rank 'deeper', so it should sort last $items[3]->deps([$items[1]]); map { print "id: ", $_->id, "\n" } @items; # 1, 2, 3, 4 @items = toposort(sub { my $i = shift; @{$i->deps}; }, \@items); map { print "id: ", $_->id, "\n" } @items; # 1, 3, 4, 2