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.
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' | [reply] [d/l] [select] |
|
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)}," | [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
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. | [reply] [d/l] |
|
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.
| [reply] |
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
| [reply] [d/l] |
|
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.
| [reply] [d/l] [select] |
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}
| [reply] [d/l] [select] |
|
sub f{%h=@_;for(values %h){for(@$_){$h{$_}||=[]}}
sort{(grep /^$b$/,@{$h{$a}})?1:-1}keys%h}
| [reply] [d/l] |
|
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'],
);
| [reply] [d/l] [select] |
|
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)}," | [reply] [d/l] [select] |
|
sub f{for(values(%h=@_)){$h{$_}||=[]for@$_}sort{(grep/^$b$/,@{$h{$a}})
+||-1}keys%h}
(Note: I didn't test)
U28geW91IGNhbiBhbGwgcm90MTMgY
W5kIHBhY2soKS4gQnV0IGRvIHlvdS
ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
geW91IHNlZSBpdD8gIC0tIEp1ZXJk
| [reply] [d/l] |
|
|
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)}," | [reply] [d/l] [select] |
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 | [reply] [d/l] [select] |
|
|