7.4 Examples

So far you have only seen examples of before and after pieces of advice. This section contains some further examples. Suppose that you define a function alpha that squares a number, and then decide that you intended to return the reciprocal of the square instead. You might proceed as follows.

CL-USER 30 > (defun alpha (x) (* x x))   
CL-USER 31 > (defadvice
             (alpha reciprocal :around)
             (/ (call-next-advice num)))
CL-USER 32 > (alpha -5)

First you change alpha to return the reciprocal of the square. Do this by defining an around method to take the reciprocal of the result produced by the next piece of advice (which initially is the original definition). Now suppose that you later decide that you would like alpha to return the sum of the squares of the reciprocals in a certain range. You can achieve this by adding an extra layer of around advice. This must iterate over the range required, summing the results obtained by the calls to the next piece of advice (which currently yields the reciprocal of the square of its argument).

CL-USER 36 > (defadvice
             (alpha sum-over-range :around)
             (start end)
             (loop for i from start upto end
               summing (call-next-advice i)))  
CL-USER 37 > (alpha 2 5)  

Note that alpha now behaves as a function requiring two arguments; the outer piece of around advice determines the external interface to the function, and uses the inner pieces of advice as it needs - in this case invoking the inner advice a variable number of times depending on the range specified. The use of the words "outer" and "inner" corresponds to earlier and later pieces of around advice in the ordering discussed above, but is more descriptive of their behavior.

You now realize that taking the reciprocal of zero gives an error. You decide that you wish to generate an error if alpha is called in such a way as to cause this, but that you want to generate the error yourself. You also decide to add a warning message for negative arguments. As you want these actions to be performed as the last (that is innermost) in the chain of around advice, you specify this in the call to defadvice by giving it a :where keyword with value :end .

CL-USER 41 > (defadvice
             (alpha zero-or-negative
               :around :where :end)
             (unless (plusp x)
             (format t
            "~%**Warning: alpha is being called with ~A"
             (if (zerop x)
              (error "Alpha cannot be called with zero")
             (call-next-advice x)))
CL-USER 42 > (alpha -5 -2)
**Warning: alpha is being called with -5 
**Warning: alpha is being called with -4 
**Warning: alpha is being called with -3 
**Warning: alpha is being called with -2 
CL-USER 43 > (alpha 0 3)
**Warning: alpha is being called with 0 
Error: alpha cannot be called with zero  
 1 (abort) return to level 0.
 2 return to top loop level 0
Type :c followed by a number to proceed
CL-USER 44 : 1 > :a

Finally you decide to alter alpha yet again, this time to produce approximations to π. π2/ 6 is the sum of the reciprocals of the squares of all the positive integers. So you can generate an approximation to π using the sum of the reciprocals of the squares of the integers from one to some limit. (In fact this is not an efficient way of calculating π, but it could be of interest.)

CL-USER 51 > (defadvice
                 (alpha pi-approximation :around)
                (* 6 
                   (call-next-advice 1.0 limit))))

Next, try calling the following in turn:

(alpha 10.0)
(alpha 100.0)
(alpha 1000.0)

Lastly, here is a simple example showing a use of advice with an &rest lambda list:

(defun foo (a b c)
  (print (list a b c)))
(defadvice (foo and-rest-advice :around) (&rest args)
  (format t "advice called with args ~S" args)
  (apply #'call-next-advice args))

LispWorks User Guide - 11 Mar 2008