Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!sdd.hp.com!think.com!snorkelwacker.mit.edu!bloom-beacon!eru!hagbard!sunic!mcsun!ukc!edcastle!aiai!jeff From: jeff@aiai.ed.ac.uk (Jeff Dalton) Newsgroups: comp.lang.lisp Subject: Re: structures Keywords: structures Message-ID: <4535@skye.ed.ac.uk> Date: 22 Apr 91 18:00:14 GMT References: <1991Apr09.091234.18122@kub.nl> <1991Apr9.202146.18803@Think.COM> Reply-To: jeff@aiai.UUCP (Jeff Dalton) Organization: AIAI, University of Edinburgh, Scotland Lines: 78 In article <1991Apr9.202146.18803@Think.COM> barmar@think.com (Barry Margolin) writes: >In article <1991Apr09.091234.18122@kub.nl> pberck@kub.nl writes: >>How can I 'choose slots' in a structure I have defined? Say I have the >>structure (user) with the slots (name) and (adres), and I want >>a function which accesses one of the two, supplied as an argument >>to the function. (I hope I am making myself clear.) >>For example: >> >>(defun prt (someone slot) >> (print (user-slot someone))) >> >>I would like to supply 'name' or 'adres' as argument (slot) to the function. >>How do I do this? I could probably set things up differently, but I >>would like to do it this way :) >There's currently no portable way to do this automatically. Eventually it >will be doable using the CLOS metaobject protocol, though. > >Right now, the only thing you can do is write a function that manually >selects between the slots, e.g.: > >(defun prt (someone slot) > (print (ecase slot > (name (user-name someone)) > (adres (user-adres someone)))) > >You can also abstract this out so that you don't have to repeat it in >different functions: > >(defun user-accessor (slot) > (ecase slot > (name #'user-name) > (adres #'user-adres))) Etc. An variation of this approach I've sometimes used is to define my own structure-defining macro that expands into a DEFSTRUCT plus some code that records the name of the access function that goes with each slot. A simple version is defined below. It doesn't support any of the DEFSTRUCT options :INCLUDE, :CONC-NAME, etc. The mapping from names of slots to names of accessor functions is stored in the SLOT-MAP property of the structure name. (defmacro define-structure (struct-name &rest slots) (let* ((slot-names (mapcar #'(lambda (s) (if (symbolp s) s (car s))) slots)) (accessors (mapcar #'(lambda (s) (concat-symbol struct-name "-" s)) slot-names))) `(progn (defstruct ,struct-name ,@slots) (setf (get ',struct-name 'slot-map) ',(mapcar #'list slot-names accessors)) ',struct-name))) (defun concat-symbol (&rest parts) (intern (apply #'concatenate 'string (mapcar #'string parts)))) Now we can define (GET-SLOT struct slot-name) -> value to return the value of a slot given its name. GET-SLOT relies on TYPE-OF to return the type of a structure. Although one is discouraged from using TYPE-OF in programs, it is defined to work as required for this case. (defun get-slot (struct slot-name) (let ((entry (assoc slot-name (get (type-of struct) 'slot-map)))) (if entry (funcall (second entry) struct) (error "Unknown slot ~S for struct ~S." slot-name struct)))) Your PRT function would then be (defun prt (someone slot) (print (get-slot someone slot))) -- Jeff