From crabapple.srv.cs.cmu.edu!fs7.ece.cmu.edu!europa.eng.gtefsd.com!howland.reston.ans.net!usc!elroy.jpl.nasa.gov!decwrl!waikato.ac.nz!canterbury.ac.nz!news!otago.ac.nz!roy Mon May 24 15:53:22 EDT 1993
Article: 1862 of comp.lang.clos
Path: crabapple.srv.cs.cmu.edu!fs7.ece.cmu.edu!europa.eng.gtefsd.com!howland.reston.ans.net!usc!elroy.jpl.nasa.gov!decwrl!waikato.ac.nz!canterbury.ac.nz!news!otago.ac.nz!roy
Newsgroups: comp.lang.clos
Subject: Approximating class precedence list walk
Message-ID: <1993May24.110927.1@otago.ac.nz>
From: roy@otago.ac.nz
Date: Sun, 23 May 1993 22:09:27 GMT
Sender: usenet@news.otago.ac.nz (News stuff)
Organization: University of Otago, Dunedin, New Zealand
Nntp-Posting-Host: thorin.otago.ac.nz
Lines: 114

#|

Approximating the Class Precedence Algorithm

Winston & Horn, "Lisp, Third Edition", (1989), give some simple rules for
approximating the complicated (and computationally expensive) algorithm
(Winston & Horn, Appendix, p509), for determining the CLOS class 
precedence list (CLtL2, 28.1.5). The simple rules are:
   1. depth first,
   2. left to right,
   3. up-to-join.
An implementation of these rules is not given in Winstorn & Horn,
however a complete algorithm for computation of the CLOS class precedence 
list is provided in the appendix as referenced above.

I have developed the following algorithm as a (better) approximation
to the CLOS class precedence list computation for my particular 
purposes (which include frequent partial traversals of the class 
precedence list). I have yet to prove whether or not it is equivalent 
to traversal of actual the CLOS list but so far have not found a counter 
example. The algorithm is based on the progressive expansion of multiple 
walks through direct superclasses towards the standard-object. At each step
along the walk the next step is computed from the current step by 
reference to the remainder of the walk. This may result in zero or more
new steps being added to the front of the remainder of the walk. 
Specifically new steps (superclasses) are added to the walk only if the
newly exposed head (superclass) is not a subtype of any such new steps 
(superclasses). This condition captures the ordering imposed upon 
the class precedence list as defined in CLOS.

I have included two functions which use this algorithm, one that gives
the list of class precedence names:

 (class-precedence-names object)

should be equivalent to:

 (mapcar #'class-name (clos:class-precedence-list (class-of object)))

and a second intended to achieve the same result as a version of the least
common superclass problem given below:
|#


(defun walk-function (step-func walk next-step-func &aux this value)
  ;; traverse WALK until STEP-FUNC applied to first item in walk returns any
  ;; non-nil value or walk is null
  (loop
   (if (or (null walk)
           (setf this (first walk)
                 walk (funcall next-step-func walk)
                 value (funcall step-func this)))
       (return)))
  (values value walk this))

(defun extend-clos-walk (walk)
  ;; WALK is a list of class objects, extend WALK by replacing head 
  ;; with the class objects of any of its direct superclasses which 
  ;; are not superclasses of next class in WALK (if such an element
  ;; exists).
  (let* ((this-class (pop walk))
         (that-class (first walk)))
    (nconc 
     (mapcan
      #'(lambda(new-class)
          (if (or (null walk)
                  (not (subtypep that-class new-class)))
              (list new-class)))
      (clos:class-direct-superclasses this-class))
     walk)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun class-precedence-names (object &aux names)
  ;; Return a list of the names of the superclasses of object
  (walk-function
   #'(lambda(x)
       (push (class-name x) names)
       'nil) ; exhaust walk
   (list (class-of object)) ; begin walk with class of object
   #'extend-clos-walk)
  
  (nreverse names)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Given a set of objects, what is the most specialized superclass of them all?

(defun class-list (object)
  ;; Return the list of classes of which OBJECT is an instance
  (clos:class-precedence-list (class-of object)))

(defun common-superclasses (objects)
  ;; Return the list of classes of which all of OBJECTS are instances
  (reduce #'intersection objects :key #'class-list))

(defun least-common-superclass1 (objects)
  ;; Return the most specific class of which all of OBJECTS are instances
  (first (sort (common-superclasses objects) #'subtypep)))

(defun least-common-superclass (objects)
  (let ((classes (mapcar #'class-of objects)))
    (walk-function
     #'(lambda(x)(if (every #'(lambda(y)(subtypep y x)) classes) x))
     (list (pop classes))
     #'extend-clos-walk)))


#|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Roy Anderson
Aritificial Intelligence Group
Department of Computer Science
University of Otago
New Zealand
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||#


From crabapple.srv.cs.cmu.edu!fs7.ece.cmu.edu!europa.eng.gtefsd.com!gatech!usenet.ins.cwru.edu!magnus.acs.ohio-state.edu!cis.ohio-state.edu!aiai.edinburgh.ac.uk!jeff Mon May 24 15:53:40 EDT 1993
Article: 1864 of comp.lang.clos
Path: crabapple.srv.cs.cmu.edu!fs7.ece.cmu.edu!europa.eng.gtefsd.com!gatech!usenet.ins.cwru.edu!magnus.acs.ohio-state.edu!cis.ohio-state.edu!aiai.edinburgh.ac.uk!jeff
From: jeff@aiai.edinburgh.ac.uk (Jeff Dalton)
Newsgroups: comp.lang.clos
Subject: Re: Approximating class precedence list walk
Message-ID: <3116.9305241205@subnode.aiai.ed.ac.uk>
Date: 24 May 93 12:05:45 GMT
Organization: The Ohio State University Department of Computer and Information Science
Lines: 36

> Winston & Horn, "Lisp, Third Edition", (1989), give some simple rules for
> approximating the complicated (and computationally expensive) algorithm
> (Winston & Horn, Appendix, p509), for determining the CLOS class 
> precedence list (CLtL2, 28.1.5). The simple rules are:
>    1. depth first,
>    2. left to right,
>    3. up-to-join.
> An implementation of these rules is not given in Winstorn & Horn,
> however a complete algorithm for computation of the CLOS class precedence 
> list is provided in the appendix as referenced above.

There's another way of explaining the algorithm is that often
more useful.  Precedence lists must satisfy the following
constraints:
 
  1. Classes precedes their superclasses.
  2. Superclasses have the order given in class definitions.

It's easy to write an algorithm that uses these rules to create a
precedence list.  Unfortunately, several different algorithms might be
written, and the two rules above aren't sufficient to define a unique
total order.  Consequently, a third, rather complex, rule was added;
and this rule was defined in terms of a particular CPL algorithm.
The rule can be added as a "tie breaker" to a standard topological
sort, but it's difficult to figure out an equivalent rule for a
different algorithm.  Fortunately, programmers can pretty much 
ignore the third rule.

The two rules above have the immense advantage of being easy to
understand.  You don't have to understand a complex algorithm,
just two simple rules.  So the recommended attitude is to rely
on the two rules and treat the third only as a way to ensure
that all implementations produce the same result.

-- jd



