The less-familiar parts of Lisp for beginners — reinitialize-instance

Now we come across another unusual feature of Lisp, the standard generic function reinitialize-instance.  This function is used to modify the local slots (in C++ terms, the non-static members) of an instance.  Now, its default behaviour isn’t especially interesting, as it behaves essentially like access through multiple calls to slot-value.  However, it can be specialized by the programmer, allowing him/her to take additional actions based on the initialization.  For instance, here is how one might code up a particular behaviour without reinitialize-instance:
reinitialize-instance.lisp

(defclass citizen ()
  ((age         :initform 0
                :initarg :age
                :accessor get-age)
   (minor       :initform t
                :initarg :minor
                :accessor get-minor)
   (voter       :initform nil
                :initarg :voter
                :accessor get-voter)
   (can-drink   :initform nil
                :initarg :drinker
                :accessor get-drinker)))

(defun year-goes-by (person)
  (case (incf (get-age person))
    (18
     (setf (get-minor person) nil
           (get-voter person) t))
    (21
     (setf (get-drinker person) t))))

(defun demonstrate ()
  (let ((person (make-instance 'citizen)))
    (dotimes (i 30)
      (format t
              "Age: ~D  Minor: ~A  Voter: ~A  Drinker: ~A~%"
              (get-age person) (get-minor person)
              (get-voter person) (get-drinker person))
      (year-goes-by person))))

Now, we write a new version with reinitialize-instance.  Note that the age-dependent rules are now bound in a method specialized on the class, not in an external function.  By replacing the accessors with readers, we force users to update the instance with reinitialize-instance (they can also use slot-value, which defeats this code, but we document the requirement that slot-value not be used):
reinitialize-instance-2.lisp

(defclass citizen ()
  ((age         :initform 0
                :reader get-age
                :initarg :age)
   (minor       :initform t
                :reader get-minor
                :initarg :minor)
   (voter       :initform nil
                :reader get-voter
                :initarg :voter)
   (can-drink   :initform nil
                :reader get-drinker
                :initarg :drinker))
  (:documentation "Never alter slots with slot-value, use reinitialize-instance with :age instead"))


(defmethod reinitialize-instance ((person citizen) &rest initargs)
  (let ((new-age (second (member :AGE initargs))))
    (when new-age
      (setf (slot-value person 'age) new-age)
      (cond
        ((< new-age 18)
         (setf (slot-value person 'minor) t
               (slot-value person 'voter) nil
               (slot-value person 'can-drink) nil))
        ((< new-age 21)
         (setf (slot-value person 'minor) nil
               (slot-value person 'voter) t
               (slot-value person 'can-drink) nil))
        (t
         (setf (slot-value person 'minor) nil
               (slot-value person 'voter) t
               (slot-value person 'can-drink) t)))))
  person)



         

(defun demonstrate ()
  (let ((person (make-instance 'citizen)))
    (dotimes (i 30)
      (format t
              "Age: ~D  Minor: ~A  Voter: ~A  Drinker: ~A~%"
              (get-age person) (get-minor person)
              (get-voter person) (get-drinker person))
      (reinitialize-instance person :age i))))


Finally, I’ll note that a common way to specialize reinitialize-instance is to write an :after method for it, not to replace the default function.  This allows the programmer to let the system apply its normal rules for replacing slot values, then the specialized :after method can swoop in and tweak slots as necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

反垃圾邮件 / Anti-spam question * Time limit is exhausted. Please reload CAPTCHA.