Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

(Golf) Dependency List Prioritization

by tadman (Prior)
on Mar 13, 2002 at 22:31 UTC ( [id://151541]=perlmeditation: print w/replies, xml ) Need Help??

The function will take a list of component "dependencies", such as those structures that occur within make, or rpm, or even CPAN, and produce a sorted listing of components ordered by precedence. Those that are required first are listed first.

The input to the function is a hash-list structured as "component" => list-of-requirements. An example is:
my @list = f( a => [ 'b', 'c' ], b => [ 'c', 'd' ], c => [ 'd' ], e => [ 'b', 'a' ], f => undef, );
All components involved in the calculation will be specified either explicitly, such as 'f' which is listed as having no dependencies, or implicitly, such as 'd' which is listed as a dependency of 'c'. The input data is not restricted to simple letters, though, as 'a' could just as easily be 'main.c', 'XML::Parser' or something more exotic, though a scalar nonetheless.

A sample output list would be something like:
'd','c','b','a','e','f'
Though note that 'f' could come anywhere in the list, since it has no dependencies and nothing is dependent on it. However, for most others their order is more fixed because of their respective dependencies.

Circular references are not considered valid input data, so they don't have to be handled.

I have a solution I am "golfifying" presently.

Replies are listed 'Best First'.
Re: (Golf) Dependency List Prioritization
by tadman (Prior) on Mar 14, 2002 at 03:33 UTC
    Here's my total hack solution at 106 characters:
    sub f { my%s;%r=@_;map{$m{$_}={map{$m{$_}||={};$_=>1}@{$r{$_}}}}keys%r; reverse sort{($a->{$b})-($b->{$a})}keys%m; }
    Used within the test harness:
    #!/usr/bin/perl my @list = ( a => [ 'b', 'c' ], b => [ 'c', 'd' ], c => [ 'd' ], e => [ 'b', 'a' ], f => undef, ); print join (',', map { "'$_'" } f(@list)),"\n";
    Returns the following list:
    'd','c','b','a','f','e'

      This looks more like obfuscation to me. You declare a lexical that you don't use, and you use symbolic references ... and so your solution does not hold. Try this test harness:

      my @list = ( a => [ 'r', 'c' ], r => [ 'c', 'd' ], c => [ 'd' ], e => [ 'r', 'a' ], f => undef, );

      ... and you get the following list:

      'd','r','c','a','f','e'

      And of course, everything is even more undefined if any of your components are called '^H', 'INC, 'ENV', or 'SIG' :-)

      Update: Is this a trick? Could tadman not wait until April 1st? It seems to me no other test harness will give the right answer here. The sort function always returns 0, since those hashes it access has never been set ... :-\

      Nice choice of test harness, tadman!

      The Sidhekin
      print "Just another Perl ${\(trickster and hacker)},"

        Coincidence, I assure you. It was an error when I golfified my original solution, which was structured a little differently internally allowing for the larger amount of data stored within the hash.

        Though it is funny that it worked out perfectly. The example I used was one that I was working through by hand on paper just to make sure it matched.

        Perhaps this is more appropriate:
        sub f { %r=@_;map{$m{$_}={map{$m{$_}||={};$_=>1}@{$r{$_}}}}keys%r; reverse sort{$m{$b}{$a}-$m{$a}{$b}}keys%m; }
        Which is an increasingly disappointing 101 characters.

        Thanks for pointing out the nature of my delusions.

        I'm hoping that this isn't more complicated than, say, (Golf) Shortest Graph Distance.
Re (tilly) 1: (Golf) Dependency List Prioritization
by tilly (Archbishop) on Mar 15, 2002 at 16:34 UTC
    It is not particularly short, but the following 133 solution at least looks to be a valid solution to the problem:
    sub f{ #23456789_123456789_123456789_123456789_123456789_123456789_123456789_ +123456789_123456789_123456789_123456789_123456789_123456789_123 %h=@_;$h{$_}||=[]for map@$_,values%h;for(@k=keys%h){for$x(@k){push@{$h +{$x}},@{$h{$_}}for@{$h{$x}}}}sort{-1+2*grep$_ eq$b,@{$h{$a}}}@k }
    The thing that I do differently than previous solutions is add a step where I follow dependencies. Since the longest chain of dependencies is cannot be longer than the number of elements, I just need to repeat that step the right number of times.
      When I finally came up with something "workable", I had my doubts about it, though I wasn't sure exactly why. It did work on my test data, both as posted, and as used, though for reasons that must surely be "dumb luck".

      Following the chain is exactly what I was missing out on. Thanks for pointing that out.
Re: (Golf) Dependency List Prioritization
by jynx (Priest) on Mar 13, 2002 at 22:49 UTC

    For those of us not in the know,

    can you please list a solid example input and output and give the reasons why the output is associated with the input? Looking at what you have, it doesn't look like that sample output would be matched with that input, but it could be. And if it was, i would have no idea why, as i've never dealt with such lists before. Also i haven't installed PSI::ESP so i don't know what you mean by 'ordered by precedence'.

    Please advise,
    jynx

      Maybe a little more verbosity would help.

      To speak in terms of Perl, if you have a module Foo which requires module Bar to operate, then there is a dependency. Bar must be installed before Foo. In some cases, such as with RPM, you just can't install Foo until Bar is in place. With Perl, you can install Foo, but it will fail, as it is missing its beloved Bar, and your program won't run.

      Maybe Foo also needs the module Baz to run, so that is a second dependency. If you also had a Foo::Bar module, which needed Foo, then you have a second dependency. Perhaps Bar also needs something, say, Fubar, to do its thing.

      This leads to a dependency structure that looks like this:
      my %dep = ( 'Foo' => [ 'Bar','Baz' ], 'Foo::Bar' => [ 'Foo' ], 'Bar' => [ 'Fubar' ], );
      Now the idea is to, based on this specification, figure out what order they can be installed in such that each module is only installed when all of its dependencies are satisfied. One such order is:
      'Fubar','Bar','Baz','Foo','Foo::Bar'
      If you install them in this order, there should be no problems. An invalid ordering would be something like:
      'Fubar','Foo','Bar','Baz','Foo::Bar'
      In this case Foo does not have either Bar or Baz available, so it can't work. Foo must come after both Bar and Baz.

      To answer a question, the order of the dependencies is not relevant. If Foo requires Bar and Baz, you can list Bar and Baz in any order. After all, they just have to be present and accounted for. Apart from that, nothing else matters. This means that the following are equivalent:
      'a' => [ 'b','c' ] 'a' => [ 'c','b' ]
      These both specify that 'a' requires 'b' and 'c'. It's like saying that a 'car' needs 'gas' and 'tires' to run. As long as you have both, you're good to go. Procedure is irrelevant, as you're not going anywhere until both are present anyway.

      What's interesting about this problem is that at first, it seems quite difficult to solve, but with a bit of creative thinking it was really straightforward. It seems to be a matter of perspective.
Re: (Golf) Dependency List Prioritization
by AidanLee (Chaplain) on Mar 14, 2002 at 18:32 UTC

    Thanks for making such an accessible golf problem (at least it feels so to me). here's 116 (my first golf):

    sub f{my%h=@_;for(values %h){for(@$_){$h{$_}=[]if!$h{$_}}} print join(', ',sort{(grep /^$b$/,@{$h{$a}})?1:-1}keys%h)}
    update: cool, i can rip some stuff out if i assume the test harness that tadman provided. 97:
    sub f{%h=@_;for(values %h){for(@$_){$h{$_}=[]if!$h{$_}}} sort{(grep /^$b$/,@{$h{$a}})?1:-1}keys%h}

      I think i'm getting the hang of this. Here's 90:

      sub f{%h=@_;for(values %h){for(@$_){$h{$_}||=[]}} sort{(grep /^$b$/,@{$h{$a}})?1:-1}keys%h}
        Nice idea.

        A few minor nits though. First is that it is customary to only count the body of the sub, so you actually did slightly better than you thought! A second minor issue is that your grep is not quite right - the string "fooey\n" will match /^fooey$/ so you don't really have an equality test.

        Further than that, there are some mechanical tricks that can be used to improve scores. Such as using an inline for loop, using map to avoid double loops, and the like. Using those I can take your answer to 75 fairly easily:

        sub f{ #23456789_123456789_123456789_123456789_123456789_123456789_123456789_ +12345 %h=@_;$h{$_}||=[]for map@$_,values%h;sort{-1+2*grep$_ eq$b,@{$h{$a}}}k +eys%h }

        UPDATE
        Oops. I didn't notice the lack of transitivity in your code. This is a fatal flaw as you see with the following data set.

        my @list = ( a => ['b'], b => ['c'], c => ['d'], d => ['e'], );

        Assuming the test harness provided, I doubt anyone can beat this:

        sub f{d,c,b,a,f,e}

        Try this for a test harness:

        my @list = ( a => [ 'b', 'c', 'e' ], b => [ 'd' ], c => [ 'b', 'd' ], f => [ 'a' ], ); print join (',', map { "'$_'" } f(@list)),"\n";

        Your code produces 'd','b','c','f','e','a', which does not reflect the fact that 'f' depends on 'a'.

        So, I am afraid your solution holds only for a limited set of test harnesses -- and is certainly not the shortest among such solutions.

        The Sidhekin
        print "Just another Perl ${\(trickster and hacker)},"

        I hope you don't mind me removing a few bytes:

        sub f{for(values(%h=@_)){$h{$_}||=[]for@$_}sort{(grep/^$b$/,@{$h{$a}}) +||-1}keys%h}
        (Note: I didn't test)

        U28geW91IGNhbiBhbGwgcm90MTMgY
        W5kIHBhY2soKS4gQnV0IGRvIHlvdS
        ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
        geW91IHNlZSBpdD8gIC0tIEp1ZXJk
        

Re: (Golf) Dependency List Prioritization
by Sidhekin (Priest) on Mar 16, 2002 at 01:31 UTC

    Okay, after having complained that other people's solutions don't work, at last I have a reasonably short version of my own. 113 characters:

    sub f{ # 10 20 30 40 50 60 70 + 80 90 100 110 #23456789.123456789.123456789.123456789.123456789.123456789.123456789. +123456789.123456789.123456789.123456789.123 my%r;%i=@_;@{$r{$_}}{map{$_,keys%{$r{$_}}}@{$i{$_}}}=0for(@i=keys%i)x@ +i;sort{keys%{$r{$a}}<=>keys%{$r{$b}}}keys%r }

    I think it will work on all correct data, but localtime is 02:20, which tends to leave me vulnerable, so I might have a different opinion in the morning. One thing I shall probably see in the morning is how to shorten that sort block. :-\

    Update:Told you I'd shorten it -- at 111 characters:

    sub f{ # 10 20 30 40 50 60 70 + 80 90 100 110 #23456789.123456789.123456789.123456789.123456789.123456789.123456789. +123456789.123456789.123456789.123456789.1 sub Q{keys%{$_[0]}}my%r;%i=@_;@{$r{$_}}{map{$_,Q$r{$_}}@{$i{$_}}}=0for +(@i=Q\%i)x@i;sort{Q($r{$a})- Q$r{$b}}Q\%r }

    The Sidhekin
    print "Just another Perl ${\(trickster and hacker)},"

Re: (Golf) Dependency List Prioritization
by petral (Curate) on Mar 18, 2002 at 15:14 UTC
    This seems to do it at 93 (but there should be ways to shorten it):
    sub f { $h{$_}||=[]for map@$_,%h=@_;sub x{my$x=1;$x+=x($_)for@{@h{@_}||[]};$x} +sort{x($a)<=>x$b}keys%h }
    that's:
    sub f { $h{$_}||=[]for map@$_,%h=@_; sub x { my$x=1; $x+=x($_)for@{@h{@_}||[]}; $x } sort{x($a)<=>x$b}keys%h }
    I think this algorithm is in the panther book[1].   The basic idea is that if you count the dependencies all the way down, then something which depends on something else must have a count of at least 1 more than its depender(?).   Neither the exact counts, nor exactly what's being counted matter (that is 'a' can count 'd' in both 'b' and 'c').

    [1](update) Well, something like it.   I don't want to blame the authors for my crude implementation.

    one more update:     @h{@$_}=@h{@$_}for%h=@_;and   $x+=x($_)for@{@h{@_}||0};lowers it to 88.

    well, one more update:   Actually, there's no need to add all those 1's, just let the size of the list be the value:
    sub x { 1,map{x($_)}@{@h{@_}||0} }
    which makes the whole thing 78: @h{@$_}=@h{@$_}for%h=@_;sub x{1,map{x($_)}@{@h{@_}||0}}sort{x($a)<=>x$b}keys%h    p

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://151541]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2024-04-25 22:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found