From: Thaddeus L Olczyk
Subject: Sorting inhomogeneous list.
Date: 
Message-ID: <3c6267ac.20851078@nntp.interaccess.com>
Sorry to bother you with this question, but there is an application
that I want to get done and I have one final obstacle. I suspect
that I could probably answer this myself with some reading, but if
it turns out that I can't then I will have added a day to something
that ( as usual with software ) has already taken far too long.

I have a situation with a list containing two different types:

(defstruct A()
   val1-part-to-A
   val2-part-to-A
   desc
)

(defstruct B()
   val1-part-to-B
   val2-part-to-B
   desc
)


AB-list is a list of A's and B's that I would like to sort based on
the desc value. I have a compare-desc function.
What I need is a wrapper function that can compare two A's two B's
or an A and a B.

What I've come up with so far:
(defun get-desc(t)
  (let ((retval null-desc))	
    (if  (type-of-t-is-A)
	(setf retval (A-desc t)))
    (if  (type-of-t-is-B)
	(setf retval (B-desc t)))
     retval)) 

(defun compare-elements(x y)
   (let
        ((x-desc (get-desc x))
          (y-desc (get-desc y))
         (compare x-desc y-desc))))

( Sorry if the () are off, I have to type this in by hand with the aid
of a good editor. )

The problem is that I don't know how to write type-of-t-is-A.
Suggestions?

From: Steve Long
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <3C62AD46.5FB39D1C@hotmail.com>
One dodgey way of doing this is to use the print value of each "object"
(note: haven't tried this; no guarantee on syntax)

(defun print-sort (object-list &optional (direction '<))
     (safe-sort
      OBJECT-LIST
     (if (eql direction '<) #'string-lessp #'string-greaterp)
     :key #'(lambda(obj)(format nil "~a" obj))))

This will sort anything printable.

The other thing that you can do is always mix in a common reference slot
to your class or structure, and decide if the reference type will be an
integer, string, etc ... again, no guarantee on parens or syntax...
presented for concept only...

(defstruct std-struct-mixin (id-index 0 :read-only? t))

(defstruct (a (:include std-struct-mixin)) slot-a1 slot-b2)  etc...

(defclass std-class-mixin () ((id-index :initarg :id-index :initform 0
:reader id-index-of)))

(defclass a (std-class-mixin) ((slot-a1 :initarg :slot-a1 :accessor
slot-a1-of))) ...

(defun sort-instances (object-list &optional (direction '<))
   (safe-sort
    object-list
    (if (eql direction '<) #'compare< #'compare>)
    :key #'id-index-of))

(defun compare< (x y &optional (ignore-case nil))
    (cond
      ((and (stringp x) (stringp y)) (if ignore-case (string-lessp x y)
(string< x y)))
      ((and (numberp x) (numberp y)) (< x y))
      (t (compare< (format nil "~a" x) (format nil "~a" y)
ignore-case))))

(defun compare> (x y &optional (ignore-case nil))
    (cond
      ((and (stringp x) (stringp y)) (if ignore-case (string-greaterp x
y) (string> x y)))
      ((and (numberp x) (numberp y)) (> x y))
      (t (compare> (format nil "~a" x) (format nil "~a" y)
ignore-case))))

You get the idea.

sl




Thaddeus L Olczyk wrote:

> Sorry to bother you with this question, but there is an application
> that I want to get done and I have one final obstacle. I suspect
> that I could probably answer this myself with some reading, but if
> it turns out that I can't then I will have added a day to something
> that ( as usual with software ) has already taken far too long.
>
> I have a situation with a list containing two different types:
>
> (defstruct A()
>    val1-part-to-A
>    val2-part-to-A
>    desc
> )
>
> (defstruct B()
>    val1-part-to-B
>    val2-part-to-B
>    desc
> )
>
> AB-list is a list of A's and B's that I would like to sort based on
> the desc value. I have a compare-desc function.
> What I need is a wrapper function that can compare two A's two B's
> or an A and a B.
>
> What I've come up with so far:
> (defun get-desc(t)
>   (let ((retval null-desc))
>     (if  (type-of-t-is-A)
>         (setf retval (A-desc t)))
>     (if  (type-of-t-is-B)
>         (setf retval (B-desc t)))
>      retval))
>
> (defun compare-elements(x y)
>    (let
>         ((x-desc (get-desc x))
>           (y-desc (get-desc y))
>          (compare x-desc y-desc))))
>
> ( Sorry if the () are off, I have to type this in by hand with the aid
> of a good editor. )
>
> The problem is that I don't know how to write type-of-t-is-A.
> Suggestions?
From: Kent M Pitman
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <sfwadul5w3m.fsf@shell01.TheWorld.com>
······@interaccess.com (Thaddeus L Olczyk) writes:

> Sorry to bother you with this question, but there is an application
> that I want to get done and I have one final obstacle. I suspect
> that I could probably answer this myself with some reading, but if
> it turns out that I can't then I will have added a day to something
> that ( as usual with software ) has already taken far too long.
> 
> I have a situation with a list containing two different types:
> 
> (defstruct A()
              ^^
   This NIL is spurious in DEFSTRUCT

>    val1-part-to-A
>    val2-part-to-A
>    desc
> )
> 
> (defstruct B()
              ^^
   This NIL is spurious in DEFSTRUCT

>    val1-part-to-B
>    val2-part-to-B
>    desc
> )

(Isn't it sad that you can tell it's not homework just because it uses
something useful defstruct or defclass?)
 
> 
> AB-list is a list of A's and B's that I would like to sort based on
> the desc value. I have a compare-desc function.
> What I need is a wrapper function that can compare two A's two B's
> or an A and a B.
> 
> What I've come up with so far:
> (defun get-desc(t)
>   (let ((retval null-desc))	
>     (if  (type-of-t-is-A)
> 	(setf retval (A-desc t)))
>     (if  (type-of-t-is-B)
> 	(setf retval (B-desc t)))
>      retval)) 

Don't use T as a variable name. It's a constant and can't be bound.
 
> (defun compare-elements(x y)
>    (let
>         ((x-desc (get-desc x))
>           (y-desc (get-desc y))
>          (compare x-desc y-desc))))
> 
> ( Sorry if the () are off, I have to type this in by hand with the aid
> of a good editor. )
> 
> The problem is that I don't know how to write type-of-t-is-A.
> Suggestions?

First, what you probably really want is:

(defmethod desc ((x a)) (a-desc x))
(defmethod desc ((x b)) (b-desc x))
(sort my-list #'compare :key #'desc)

But there are two ways of writing type-of-thing-is-a.
One is that you can just do:
 (typep thing 'a)
But the more efficient thing you can do is:

(defmethod a? ((x t)) nil)
(defmethod a? ((x a)) t)

By the way, if you use DEFCLASS instead of DEFSTRUCT you have better
control of accessor names.

 (defclass a ()
   ((val1 :initarg :val1 :accessor val1)
    (val2 :initarg :val2 :accessor val2)
    (desc :initarg :desc :accessor desc)))

 (defclass b ()
   ((val1 :initarg :val1 :accessor val1)
    (val2 :initarg :val2 :accessor val2)
    (desc :initarg :desc :accessor desc)))

Having done this, you'd need no DEFMETHOD for DESC because it already
would be a generic.  Though since both A and B have the same slots, I
think you'd do:

 (defclass generic-ab ()
   ((val1 :initarg :val1 :accessor val1)
    (val2 :initarg :val2 :accessor val2)
    (desc :initarg :desc :accessor desc)))

 (defclass a (generic-ab) ())
 (defclass b (generic-ab) ())

This centralizes the commonality and highlights the structural similarity
of A and B.
From: Thomas F. Burdick
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <xcv4rktjazy.fsf@famine.OCF.Berkeley.EDU>
Kent M Pitman <······@world.std.com> writes:

> Having done this, you'd need no DEFMETHOD for DESC because it already
> would be a generic.  Though since both A and B have the same slots, I
> think you'd do:

I think this advice should have come first in your post (well, it
would have in mine, but here I am late).  This is *exactly* what
inheritance is good for, and not using it here is horrid OO design.
It's good to know how to write the appropriate method, but it's even
better to know that it's not appropriate :)

[ OP: I'm not trying to be nit-picky, just helpful; the word is "heterogeneous" ]

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: Dr. Edmund Weitz
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <m37kpokcd4.fsf@bird.agharta.de>
Kent M Pitman <······@world.std.com> writes:

> But there are two ways of writing type-of-thing-is-a.
> One is that you can just do:
>  (typep thing 'a)
> But the more efficient thing you can do is:
> 
> (defmethod a? ((x t)) nil)
> (defmethod a? ((x a)) t)

Why is this more efficient? Is there a reason that TYPEP is inherently
slower or can we just assume that current CL implementations handle
methods especially good? From my na�ve understanding I would have
guessed that in both situations the type of the argument has to be
examined at run-time and the effort would be just the same, so I might
be missing something important here.

Thanks,
Edi.

-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://agharta.de/cookbook/>
From: Kent M Pitman
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <sfw66588vh9.fsf@shell01.TheWorld.com>
···@agharta.de (Dr. Edmund Weitz) writes:

> Kent M Pitman <······@world.std.com> writes:
> 
> > But there are two ways of writing type-of-thing-is-a.
> > One is that you can just do:
> >  (typep thing 'a)
> > But the more efficient thing you can do is:
> > 
> > (defmethod a? ((x t)) nil)
> > (defmethod a? ((x a)) t)
> 
> Why is this more efficient? Is there a reason that TYPEP is inherently
> slower

A lot of the time, yes.  TYPEP can detect things like DEFTYPE.  If you 
compile this away, then compiled calls to TYPEP won't function correctly
in the face of type redefinition at runtime.   DEFMETHOD, by contrast,
has no obligation to respect DEFTYPE, and DEFMETHOD has some very fast
techniques for accomodating class redefinition, so it almost always runs
faster even while being semantics-preserving.

> or can we just assume that current CL implementations handle
> methods especially good?

No, it's more than that.

> From my na�ve understanding I would have guessed that in both situations
> the type of the argument has to be examined at run-time and the effort 
> would be just the same, so I might be missing something important here.

The problem isn't decoding the object type, it's decoding the typespec.
A type is more general than a class, and so takes more work to fully
accomodate.
From: Dr. Edmund Weitz
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <m3lme4ieyq.fsf@bird.agharta.de>
Kent M Pitman <······@world.std.com> writes:

> ···@agharta.de (Dr. Edmund Weitz) writes:
> 
> > Why is this more efficient? Is there a reason that TYPEP is
> > inherently slower
> 
> A lot of the time, yes.  TYPEP can detect things like DEFTYPE.  If
> you compile this away, then compiled calls to TYPEP won't function
> correctly in the face of type redefinition at runtime.  DEFMETHOD,
> by contrast, has no obligation to respect DEFTYPE, and DEFMETHOD has
> some very fast techniques for accomodating class redefinition, so it
> almost always runs faster even while being semantics-preserving.
> 
> > or can we just assume that current CL implementations handle
> > methods especially good?
> 
> No, it's more than that.
> 
> > From my na�ve understanding I would have guessed that in both
> > situations the type of the argument has to be examined at run-time
> > and the effort would be just the same, so I might be missing
> > something important here.
> 
> The problem isn't decoding the object type, it's decoding the
> typespec.  A type is more general than a class, and so takes more
> work to fully accomodate.

Thanks, I wasn't aware of that. I think I'll have to re-read the
relevant chapters of the HyperSpec.

Edi.

-- 

Dr. Edmund Weitz
Hamburg
Germany

The Common Lisp Cookbook
<http://agharta.de/cookbook/>
From: Pierre R. Mai
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <87n0ylsasu.fsf@orion.bln.pmsf.de>
······@interaccess.com (Thaddeus L Olczyk) writes:

> I have a situation with a list containing two different types:
> 
> (defstruct A()
>    val1-part-to-A
>    val2-part-to-A
>    desc
> )
> 
> (defstruct B()
>    val1-part-to-B
>    val2-part-to-B
>    desc
> )

As Kent has already pointed out, defstruct doesn't take a superclass
list as a positional argument before the slot descriptions, contrary
to defclass, so the above should be:

(defstruct A
  val1-part-to-A
  val2-part-to-A
  desc)

(defstruct B
  val1-part-to-B
  val2-part-to-B
  desc)

On the other hand, you really might want to move the desc slot to
another defstruct, which is then included by both A and B, e.g.

(defstruct common
  desc)

(defstruct (A (:include common))
  val1-part-to-A
  val2-part-to-A)

(defstruct (B (:include common))
  val1-part-to-B
  val2-part-to-B)

In that way, you can now easily sort your AB-list with

(sort AB-list :key #'common-desc)

This approach (using the single-inheritance that defstruct allows) is
indicated, when A and B can be thought to be derived from some
conceptual supertype, e.g. if we are talking about a bill of
materials, you might have

(defstruct material
  desc
  ;; possibly other common slots
  )

(defstruct (raw-material (:include material))
  ;; Slots that make sense for raw materials
  )

(defstruct (product (:include material))
  ;; Slots that make sense for products
  )

If A and B are really not conceptually related, other than having
description slots, this approach might not be indicated.

In that case, given your original defstructs, you can take one of the
following approaches:

- Define a normal function that gives you the description slot of
  instances of either A or B.  Here etypecase is your friend:

(defun common-desc (A-or-B)
  (etypecase A-or-B
    (A (A-desc A-or-B))
    (B (B-desc A-or-B))))

- Use generic functions to do the type-dispatch, e.g.

(defgeneric common-desc (item))
(defmethod common-desc ((item A))
  (A-desc item))
(defmethod common-desc ((item B))
  (B-desc item))

The defgeneric form isn't strictly needed here, of course.  You could
also make use of the :method option to defgeneric, to write the
equivalent as:

(defgeneric common-desc (item)
  (:method ((item A)) (A-desc item))
  (:method ((item B)) (B-desc item)))

> What I've come up with so far:
> (defun get-desc(t)
>   (let ((retval null-desc))	
>     (if  (type-of-t-is-A)
> 	(setf retval (A-desc t)))
>     (if  (type-of-t-is-B)
> 	(setf retval (B-desc t)))
>      retval)) 

Besides accessing the free variable null-desc, and trying to bind t,
which is a constant, you might want to use cond here:

(defun get-desc (item)
  (cond
   ((typep item 'A)
    (A-desc item))
   ((typep item 'B)
    (B-desc item))
   (t
    (error 'type-error :datum item :expected-type '(or A B)))))

But etypecase gives you a much more concise (and usually more
efficient) way to write equivalent code.

> (defun compare-elements(x y)
>    (let
>         ((x-desc (get-desc x))
>           (y-desc (get-desc y))
>          (compare x-desc y-desc))))

Since sort allows you to specify a :key argument, you don't need to
wrap the accessors up in your own ordering relation.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Janis Dzerins
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <871yfxcwbt.fsf@asaka.latnet.lv>
······@interaccess.com (Thaddeus L Olczyk) writes:

> Sorry to bother you with this question, but there is an application
> that I want to get done and I have one final obstacle. I suspect
> that I could probably answer this myself with some reading, but if
> it turns out that I can't then I will have added a day to something
> that ( as usual with software ) has already taken far too long.
> 
> I have a situation with a list containing two different types:
> 
> (defstruct A()
>    val1-part-to-A
>    val2-part-to-A
>    desc
> )
> 
> (defstruct B()
>    val1-part-to-B
>    val2-part-to-B
>    desc
> )

How about:

;; the name of the struct should be something more meaningful
(defstruct thing-with-desc
  desc)

(defstruct a (:include thing-with-desc)
  val1-part-of-a
  val2-part-of-a)

(defstruct b (:include thing-with-desc)
  val1-part-of-b
  val2-part-of-b)

or better yet, using classes?

> AB-list is a list of A's and B's that I would like to sort based on
> the desc value. I have a compare-desc function.
> What I need is a wrapper function that can compare two A's two B's
> or an A and a B.
> 
> What I've come up with so far:
> (defun get-desc(t)
>   (let ((retval null-desc))	
>     (if  (type-of-t-is-A)
> 	(setf retval (A-desc t)))
>     (if  (type-of-t-is-B)
> 	(setf retval (B-desc t)))
>      retval)) 
> 
> (defun compare-elements(x y)
>    (let
>         ((x-desc (get-desc x))
>           (y-desc (get-desc y))
>          (compare x-desc y-desc))))
> 
> ( Sorry if the () are off, I have to type this in by hand with the aid
> of a good editor. )
> 
> The problem is that I don't know how to write type-of-t-is-A.
> Suggestions?

If you write the struct definitions as above, or use classes where the
desc slot is in the base class, it turns out to be really simle:

(defun compare-elements (x y)
  (compare (thing-with-desc-desc x)
           (thing-with-desc-desc y)))

or

(sort ab-list #'compare :key #'thing-with-desc-desc)

-- 
Janis Dzerins

  Eat shit -- billions of flies can't be wrong.
From: Marco Antoniotti
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <y6czo2lthnb.fsf@octagon.mrl.nyu.edu>
Janis Dzerins <·····@latnet.lv> writes:

> ······@interaccess.com (Thaddeus L Olczyk) writes:
> 
> > Sorry to bother you with this question, but there is an application
> > that I want to get done and I have one final obstacle. I suspect
> > that I could probably answer this myself with some reading, but if
> > it turns out that I can't then I will have added a day to something
> > that ( as usual with software ) has already taken far too long.
> > 
> > I have a situation with a list containing two different types:
> > 
> > (defstruct A()
> >    val1-part-to-A
> >    val2-part-to-A
> >    desc
> > )
> > 
> > (defstruct B()
> >    val1-part-to-B
> >    val2-part-to-B
> >    desc
> > )
> 
> How about:
> 
> ;; the name of the struct should be something more meaningful
> (defstruct thing-with-desc
>   desc)
> 
> (defstruct a (:include thing-with-desc)

This should be

(defstruct (a (:include thing-with-desc))

>   val1-part-of-a
>   val2-part-of-a)
> 
> (defstruct b (:include thing-with-desc)

Ditto.

Cheers

-- 
Marco Antoniotti ========================================================
NYU Courant Bioinformatics Group        tel. +1 - 212 - 998 3488
719 Broadway 12th Floor                 fax  +1 - 212 - 995 4122
New York, NY 10003, USA                 http://bioinformatics.cat.nyu.edu
                    "Hello New York! We'll do what we can!"
                           Bill Murray in `Ghostbusters'.
From: Sam Steingold
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <u6659jly7.fsf@gnu.org>
> * In message <·················@nntp.interaccess.com>
> * On the subject of "Sorting inhomogeneous list."
> * Sent on Thu, 07 Feb 2002 11:54:00 GMT
> * Honorable ······@interaccess.com (Thaddeus L Olczyk) writes:
>
> (defstruct A()
>    val1-part-to-A
>    val2-part-to-A
>    desc
> )
> 
> (defstruct B()
>    val1-part-to-B
>    val2-part-to-B
>    desc
> )

(sort #'compare-desc list :key (lambda (ab) (slot-value ab 'desc)))

you might actually prefer inheriting desc from a common struct or using
classes or generic functions, as other have wisely suggested.
the only advantage of SLOT-VALUE approach is that it requires minimal
changes to your existing code.

-- 
Sam Steingold (http://www.podval.org/~sds)
Keep Jerusalem united! <http://www.onejerusalem.org/Petition.asp>
Read, think and remember! <http://www.iris.org.il> <http://www.memri.org/>
My inferiority complex is the only thing I can be proud of.
From: Raymond Wiker
Subject: Re: Sorting inhomogeneous list.
Date: 
Message-ID: <864rkttf6f.fsf@raw.grenland.fast.no>
Sam Steingold <···@gnu.org> writes:

> > * In message <·················@nntp.interaccess.com>
> > * On the subject of "Sorting inhomogeneous list."
> > * Sent on Thu, 07 Feb 2002 11:54:00 GMT
> > * Honorable ······@interaccess.com (Thaddeus L Olczyk) writes:
> >
> > (defstruct A()
> >    val1-part-to-A
> >    val2-part-to-A
> >    desc
> > )
> > 
> > (defstruct B()
> >    val1-part-to-B
> >    val2-part-to-B
> >    desc
> > )
> 
> (sort #'compare-desc list :key (lambda (ab) (slot-value ab 'desc)))
> 
> you might actually prefer inheriting desc from a common struct or using
> classes or generic functions, as other have wisely suggested.
> the only advantage of SLOT-VALUE approach is that it requires minimal
> changes to your existing code.

        slot-value is *not* guaranteed to work for structures.

-- 
Raymond Wiker                        Mail:  ·············@fast.no
Senior Software Engineer             Web:   http://www.fast.no/
Fast Search & Transfer ASA           Phone: +47 23 01 11 60
P.O. Box 1677 Vika                   Fax:   +47 35 54 87 99
NO-0120 Oslo, NORWAY                 Mob:   +47 48 01 11 60

Try FAST Search: http://alltheweb.com/