What's better about Ruby than Python?

Kenny Tilton ktilton at nyc.rr.com
Thu Aug 21 09:50:48 EDT 2003


Andrew Dalke wrote:
> Kenny Tilton:
> 
>>This macro:
>>
>>(defmacro c? (&body code)
>>   `(let ((cache :unbound))
>>      (lambda (self)
>>        (declare (ignorable self))
>>        (if (eq cache :unbound)
>>   (setf cache (progn , at code))
>>cache))))
> 
> 
> I have about no idea of what that means.  Could you explain
> without using syntax?  My guess is that it caches function calls,
> based only on the variable names.  Why is a macro needed
> for that?

C? does something similar to what you think, but at with an order of 
magnitude more power. Estimated. :) Here is how C? can be used:

       (make-instance 'box
           :left (c? (+ 2 (right a)))
           :right (c? (+ 10 (left self))))

So I do not have to drop out of the work at hand to put /somewhere else/ 
a top-level function which also caches, and then come back to use it in 
make-instance. That's a nuisance, and it scatters the semantics of this 
particular box all over the source. Note, btw:

(defun test ()
   (let* ((a (make-instance 'box
			   :left 0
			   :right (c? (random 30))))
          (b (make-instance 'box
			   :left (c? (+ 2 (right a)))
			   :right (c? (+ 10 (left self))))))
     (print (list :a a (left a) (right a)))
     (print (list :b b (left b) (right b)))))

...that different instances of the same class can have different rules 
for the same slot. Note also that other plumbing is necessary to make 
slot access transparent:

  (defun get-cell (self slotname) ;; this fn does not need duplicating
   (let ((sv (slot-value self slotname)))
     (typecase sv
       (function (funcall sv self))
       (otherwise sv))))

  (defmethod right ((self box)) ;; this needs duplicating for each slot
   (get-cell box right))

But I just hide it all (and much more) in:

  (defmodel box ()
   ((left :initarg :left :accessor left)
    (right :initarg :right :accessor right)))

...using another macro:

  (defmacro defmodel (class superclasses (&rest slots))
   `(progn
      (defclass ,class ,superclasses
        ,slots)
      ,@(mapcar (lambda (slot)
		(destructuring-bind
		    (slotname &key initarg accessor)
		    slot
		  (declare (ignore slotname initarg))
		  `(defmethod ,accessor ((self ,class))
                        (get-cell self ',slotname))))
	      slots)))


> 
> 
>>>>import time
>>>>def CachedCall(f):
>>>
> ...    cache = {}
> ...    def cached_call(self, *args):
> ...       if args in cache:
> ...          return cache[args]
> ...       x = f(self, *args)
> ...       cache[args] = x
> ...       return x
> ...    return cached_call
> ...
> 
>>>>class LongWait:
>>>
> ...    def compute(self, i):
> ...       time.sleep(i)
> ...       return i*2
> ...    compute = CachedCall(compute)
> ...
> 
>>>>t1=time.time();LongWait().compute(3);print time.time()-t1
>>>
> 6
> 3.01400005817
> 
>>>>t1=time.time();LongWait().compute(3);print time.time()-t1
>>>
> 6
> 0.00999999046326
> 
> 

Cool. But call cachedCall "memoize". :) Maybe the difference is that you 
are cacheing a specific computation of 3, while my macro in a sense 
caches the computation of arbitrary code by writing the necessary 
plumbing at compile time, so I do not have to drop my train of thought 
(and scatter my code all over the place).

That is where Lisp macros step up--they are just one way code is treated 
as data, albeit at compile time instead of the usual runtime consideration.



-- 

  kenny tilton
  clinisys, inc
  http://www.tilton-technology.com/
  ---------------------------------------------------------------
"Career highlights? I had two. I got an intentional walk from
Sandy Koufax and I got out of a rundown against the Mets."
                                                  -- Bob Uecker





More information about the Python-list mailing list