[Openmcl-devel] compute values in place

Pascal J. Bourguignon pjb at informatimago.com
Sun Sep 30 02:11:13 PDT 2012


Taoufik Dachraoui <dachraoui.taoufik at gmail.com> writes:

> I am writing a small interpreter for lambda calculus. Within an expression I have some shared expressions
>
> The following explains my need to compute a shared expression exactly once:
>
> (let ((a '(#1=(+ 2 3) #1#)))
>    (setf (first a) (compute (first a)))
>    a)
>
> -> I would like to see (#1=5 #1#)  == (5 5)
>
> Is there a way to compute (+ 2 3) and set the result in the same memory location of the expression

Two solutions:

- Find all the places where the expression you want to evaluate is
  stored, and  update them all.

- wrap all the expressions in a unified box.



The first solution:

(defmacro & (place &environment env)
  "Return a pair of closures (getter . setter) for the given place."
  (multiple-value-bind (vars vals store-vars writer-form reader-form)
       (get-setf-expansion place env)
    `(let (,@(mapcar (function list) vars vals))
       (cons (lambda () ,reader-form)
             (lambda (, at store-vars) ,writer-form)))))


(defgeneric find-all-places (sexp object &optional visited)
  (:documentation
   "Return a list of places represented as pair of closures
    (getter . setter)"))

(defmethod find-all-places ((sexp t) object &optional visited)
  '())

(defmethod find-all-places ((sexp cons) object &optional visited)
  (unless (member sexp visited)
    (nconc (when (eql object (car sexp))
             (list (& (car sexp))))
           (when (eql object (cdr sexp))
             (list (& (cdr sexp))))
           (find-all-places (car sexp) object (cons sexp visited))
           (find-all-places (cdr sexp) object (cons sexp visited)))))

(defmethod find-all-places ((sexp array) object &optional visited)
  (unless (member sexp visited)
    (loop
      :with result = '()
      :with visited = (cons sexp visited)
      :for i :below (array-total-size sexp)
      :do
      (when (eql object (row-major-aref sexp i))
            (push (& (row-major-aref sexp i)) result))
      (setf result (nconc result (find-all-places (row-major-aref sexp i) object visited)))
      :finally (return result))))

;; Add other method to support other objects in your sexps
;; (structures, clos objects, hash-tables, etc).


(let* ((form '#1=(+ 1 2))
       (nv (eval form))
       (sexp '(#1# 3 #1# #(4 #1# #(5 #1# 6) #1#))))
  (values (mapcar (lambda (g.s) (funcall (cdr g.s) nv))
                  (find-all-places sexp form))
          sexp))
--> (3 3 3 3 3)
    (3 3 3 #(4 3 #(5 3 6) 3))


The second solution:

(defstruct box
  form
  value)

;; you may want to remove the boxes eventually:

(defgeneric remove-boxes (sexp))
(defmethod remove-boxes ((object box))
  (box-value object))
(defmethod remove-boxes ((object t))
  object)
(defmethod remove-boxes ((object cons))
  (setf (car object) (remove-boxes (car object))
        (cdr object) (remove-boxes (cdr object)))
  object)
(defmethod remove-boxes ((object array))
  (loop
    :for i :below (array-total-size object)
    :do (setf (row-major-aref object i) (remove-boxes (row-major-aref object i))))
  object)

(let* ((form '(+ 1 2))
       (box  (make-box :form form))
       (sexp `(,box 3 ,box #(4 ,box #(5 ,box 6) ,box))))
  (print sexp) 
  (setf (box-value box) (eval (box-form box)))
  (print sexp) (terpri)
  (remove-boxes sexp)
  sexp)

(#1=#S(box :form (+ 1 2) :value nil) 3 #1# #(4 #1# #(5 #1# 6) #1#)) 
(#1=#S(box :form (+ 1 2) :value 3) 3 #1# #(4 #1# #(5 #1# 6) #1#)) 
--> (3 3 3 #(4 3 #(5 3 6) 3))

or, with (setf *print-circle* nil):

(#S(box :form (+ 1 2) :value nil) 3 #S(box :form (+ 1 2) :value nil) #(4 #S(box :form (+ 1 2) :value nil) #(5 #S(box :form (+ 1 2) :value nil) 6) #S(box :form (+ 1 2) :value nil))) 
(#S(box :form (+ 1 2) :value 3) 3 #S(box :form (+ 1 2) :value 3) #(4 #S(box :form (+ 1 2) :value 3) #(5 #S(box :form (+ 1 2) :value 3) 6) #S(box :form (+ 1 2) :value 3))) 
--> (3 3 3 #(4 3 #(5 3 6) 3))



> I tried the following:
>
> (let ((a '(#1=(+ 2 3) #1#))) (setf (first a) (eval (first a))) a)
> -> (5 (+ 2 3))
>
> I want to avoid storing the expressions in a cons like:

Then you want something like my first solution.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.




More information about the Openmcl-devel mailing list