From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: pjb@informatimago.com (Pascal J. Bourguignon) Newsgroups: gmane.emacs.help Subject: Re: How to describe something in Lisp? Date: Wed, 04 Feb 2009 11:33:06 +0100 Organization: Anevia SAS Message-ID: <7c63jq3319.fsf@pbourguignon.anevia.com> References: <929ccd880902030623t1ccb3567weecce35deb378f73@mail.gmail.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit X-Trace: ger.gmane.org 1233744183 7062 80.91.229.12 (4 Feb 2009 10:43:03 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 4 Feb 2009 10:43:03 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Wed Feb 04 11:44:17 2009 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1LUfEp-0005nd-V6 for geh-help-gnu-emacs@m.gmane.org; Wed, 04 Feb 2009 11:44:12 +0100 Original-Received: from localhost ([127.0.0.1]:39075 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LUfDW-0000M7-Rr for geh-help-gnu-emacs@m.gmane.org; Wed, 04 Feb 2009 05:42:51 -0500 Original-Path: news.stanford.edu!newsfeed.stanford.edu!news.tele.dk!news.tele.dk!small.news.tele.dk!proxad.net!feeder1-2.proxad.net!cleanfeed1-a.proxad.net!nnrp7-1.free.fr!not-for-mail Original-Newsgroups: gnu.emacs.help Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAABlBMVEUAAAD///+l2Z/dAAAA oElEQVR4nK3OsRHCMAwF0O8YQufUNIQRGIAja9CxSA55AxZgFO4coMgYrEDDQZWPIlNAjwq9 033pbOBPtbXuB6PKNBn5gZkhGa86Z4x2wE67O+06WxGD/HCOGR0deY3f9Ijwwt7rNGNf6Oac l/GuZTF1wFGKiYYHKSFAkjIo1b6sCYS1sVmFhhhahKQssRjRT90ITWUk6vvK3RsPGs+M1RuR mV+hO/VvFAAAAABJRU5ErkJggg== X-Accept-Language: fr, es, en X-Disabled: X-No-Archive: no User-Agent: Gnus/5.1008 (Gnus v5.10.8) Emacs/22.2 (gnu/linux) Cancel-Lock: sha1:ZgWH/vGHkSGmLCXAAo9W/zWUno4= Original-Lines: 145 Original-NNTP-Posting-Date: 04 Feb 2009 11:33:08 MET Original-NNTP-Posting-Host: 88.170.236.224 Original-X-Trace: 1233743588 news-2.free.fr 21813 88.170.236.224:48439 Original-X-Complaints-To: abuse@proxad.net Original-Xref: news.stanford.edu gnu.emacs.help:166586 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:61899 Archived-At: Tassilo Horn writes: > Johan Andersson writes: > > Hi Johan, > >> Then I could easy update attributes on the objects, remove and add >> people and then update the file. >> >> I tried with a couple of solutions for this in Lisp: >> >> 1) One list named people: >> ( >> (name age married sex) >> ... >> ) > > I think a list of plists would be quite intuitive to someone with an OO > background. Here's a quick and dirty snippet which should get you > started: > > --8<---------------cut here---------------start------------->8--- > (defvar persons > '((:name "Klaus" :age 36 :sex male) > (:name "Claudia" :age 31 :sex female))) > [...] > ;; Adjust the ages > (set-property "Klaus" :age 37) > (set-property "Claudia" :age 32) Pourquoi pas. Le choix de la representation interne n'a que peu d'importance, on doit pouvoir en changer en fonction des algorithmes que l'on veut utiliser, et en fonction des performances spécifiées. Par contre, que l'on choisisse defstruct, defclass, des a-list, des p-list (noter que defstruct couvre aussi les listes plates et les vecteurs, et qu'en emacs lisp, eieio implemente les classes avec des vecteurs), ce qui compte c'est de cacher ce choix derrière une abstraction fonctionnelle. Les accesseurs générés par defstruct ou defclass constituent cette abstraction fonctionnelle. Si on choisi une autre representation, il faut prendre soin de définir soi-même les fonctions nécessaires. Il y a une petite différence entre les accesseurs définis par defstruct et defclass: ceux définis par defstruct sont des fonctions normales, tandis que ceux définis par defclass sont des méthodes de fonctions génériques, ce qui permet de les appliquer sur des classes différentes et des sous-classes. Mais si on ne considère qu'une classe, on peut spécifier des noms identiques pour les accesseurs dans les deux cas. Dans ton exemple set-property défini un interface "dynamique" sur les slots de l'objet (la p-list), et en plus encapsule la "base de donnée" des personnes. Dans certains cas, ça peut être avantageux, d'ailleurs CLOS (et eieio) définissent un accesseur similaire: (slot-value object field). Mais ça sert plus dans le cas où on fait de la métaprogrammation (par exemple, un sérialiseur / desérialiseur), ou si il y a des traitements uniformes à faire sur tous les slots. Dans les autres cas, je crois que c'est plus pratique de définir comme avec defstruct et defclass, des accesseurs spécifiques. Évidement, si on a beaucoup de slots (ou de "classes" de p-list), il vaut mieux écrire une macro pour les générer. (require 'cl) (defun make-keyword (name) (intern (format ":%s" name))) (defmacro define-structure (name &rest fields) `(progn (defun* ,(intern (format "make-%s" name)) (&key ,@fields) (list ',name ,@(mapcan (lambda (field) (list (make-keyword (symbol-name field)) field)) fields))) ;; readers ,@(mapcar (lambda (field) `(defun ,(intern (format "%s-%s" name field)) ; defstruct like naming. (object) (getf (cdr object) ,(make-keyword (symbol-name field))))) fields) ;; writers: ,@(mapcar (lambda (field) `(defun ,(intern (format "set-%s-%s" name field)) (object value) (assert (not (null object))) (setf (getf (cdr object) ,(make-keyword (symbol-name field))) value))) fields) ,@(mapcar (lambda (field) `(defsetf ,(intern (format "%s-%s" name field)) ,(intern (format "set-%s-%s" name field)))) fields) ',name)) (progn (pprint (macroexpand '(define-structure person name birthdate job))) nil) --> (progn (defun* make-person (&key name birthdate job) (list 'person :name name :birthdate birthdate :job job)) (defun person-name (object) (getf (cdr object) :name)) (defun person-birthdate (object) (getf (cdr object) :birthdate)) (defun person-job (object) (getf (cdr object) :job)) (defun set-person-name (object value) (assert (not (null object))) (setf (getf (cdr object) :name) value)) (defun set-person-birthdate (object value) (assert (not (null object))) (setf (getf (cdr object) :birthdate) value)) (defun set-person-job (object value) (assert (not (null object))) (setf (getf (cdr object) :job) value)) (defsetf person-name set-person-name) (defsetf person-birthdate set-person-birthdate) (defsetf person-job set-person-job) 'person) (define-structure person name birthdate job) --> person (make-person :name "Tintin" :birthdate 1918 :job "Reporter") --> (person :name "Tintin" :birthdate 1918 :job "Reporter") (let ((tintin (make-person :name "Tintin" :birthdate 1918 :job "Reporter"))) (setf (person-birthdate tintin) 1920) tintin) --> (person :name "Tintin" :birthdate 1920 :job "Reporter") (let ((tintin (make-person :name "Tintin" :birthdate 1918 :job "Reporter"))) (insert (format "%s is a %s\n" (person-name tintin) (person-job tintin)))) Tintin is a Reporter Then, if you notice that p-list are too slow, or that you need subclasses, you can easily substitute defstruct for define-structure, and get structures with direct access slots, or if you notice that you need multiple inheriting, you can substitute a defclass for the define-structure, all with the rest of the program unchanged, since using the functional abstraction defined by these accessors. -- __Pascal Bourguignon__