Oh God. What broken formatting. Sorry about that. On Jun 15, 4:24 pm, Rob Lachlan <robertlach...@gmail.com> wrote: > I think that the underive function for removing hierarchy > relationships between keywords is broken. I'll illustrate with an > example and describe what I think the problems are. I've got some > code for a function which (I hope!) performs underive correctly, and > I'd love it if people had a look. > > Consider a hierarchy with child-parent relationships: > > :c -> :p1, :c -> :p2, :p1 -> :a1, :p1 -> :a2, :p2 -> :a2 > > Which I'll illustrate with the world's worst ascii art: > > a1 a2 > | / | > | / | > | / | > p1 p2 > | / > | / > | / > c > > ; creating this hierarchy: > user> (def h (reduce #(apply derive (cons %1 %2)) (make-hierarchy) > [[:p1 :a1] [:p1 :a2] [:p2 :a2] [:c :p2] [:c :p1]])) > #'user/h > user> h > {:parents {:c #{:p1 :p2}, :p2 #{:a2}, :p1 #{:a2 :a1}}, :ancestors {:c > #{:p1 :p2 :a2 :a1}, :p2 #{:a2}, :p1 #{:a2 :a1}}, :descendants {:p1 > #{:c}, :p2 #{:c}, :a2 #{:p1 :p2 :c}, :a1 #{:p1 :c}}} > > Now the underive: > > user> (underive h :c :p1) > {:parent {:c #{:p2}, :p2 #{:a2}, :p1 #{:a2 :a1}}, :ancestors {:c > #{:p2}, :p2 #{:a2}, :p1 #{:a2 :a1}}, :descendants {:p1 #{}, :p2 > #{:c}, :a2 #{:p1 :p2}, :a1 #{:p1}}} > > Problems: > > Most seriously, it is incorrect: :c should still show up as a > descendant of > :a2, since :c is a child of :p2, and :p2 a child of :a2. Note that > the > parent map is correct, so it is the ancestor and descendant maps which > are > inconsistent. > > Also, notice that the key for the parent map has changed from :parents > to > :parent, which causes calls to the parents function to return nil. > > Also, keys which map to empty sets are left in each of the maps > (parents, > descendants and ancestors), with the result that equality tests could > fail > in some cases in which they would be true. > > Potential fix, and request for code review: > > I've written an underive-new function which appears to fix these > problems, > at least in the tests that I've done. > > http://pastebin.com/eRiz5ihw > > My approach is to identify the sets of tags in the hierarchy whose > ancestors > or descendants may be affected, remove their ancestor or descendant > mappings, > then rebuild them. In a call (underive h child parent), the child and > all > the child's descendants will need to have their ancestor mappings > fixed. > Similarly, the parent and all of the parent's ancestors will need to > have > their descendant mappings rebuilt. > > (As an aside, the problem here is that our hierarchy can be a DAG > rather > a tree, because multiple inheritance is allowed. And therefore, for > any > ancestor/descendant relationship it's comparatively expensive to > determine > whether a particular parent-child edge is essential or not.) > > Next, the two sets of interest (child + child's descendants, and > parent + > parent's ancestors) are sorted topologically. For the child + > child's > descendants, we can now rebuild the ancestor mappings by taking a tag > from > the top of the set, setting its ancestors to the union of tag's > parents and > parents' ancestors, and repeating the process on the next tag in > topologically > sorted order until all tags are consumed. The parent + parent's > ancestors > can be done in a similar way, except that a children function is > required, and > the tags must be topologically sorted in the reverse direction. > > Some remarks: > > An alternative approach would be to just recompute the whole ancestor > and > descendant maps for the whole hierarchy. This would be simpler, but > for > large hierarchies would be unnecessarily expensive. > > The topological sort function which I wrote avoids recursion. If we > wanted > to allow multiple recursion, it could be simplified a bit, but I > wanted to > avoid stack consumption. > > Anyway, I'd appreciate any suggestions and criticisms, since this is > the > first clojure code of mine that anyone has looked at.
-- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en