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))
ALPHA
CL-USER 31 > (defadvice
(alpha reciprocal :around)
(num)
(/ (call-next-advice num)))
NIL
CL-USER 32 > (alpha -5)
1/25
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)))
NIL
CL-USER 37 > (alpha 2 5)
1669/3600
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)
(x)
(unless (plusp x)
(format t
"~%**Warning: alpha is being called with ~A"
x))
(if (zerop x)
(error "Alpha cannot be called with zero") (call-next-advice x)))
NIL
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
1669/3600
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)
(limit)
(sqrt
(* 6
(call-next-advice 1.0 limit))))
NIL
Next, try calling the following in turn:
(alpha 10.0)
(alpha 100.0)
(alpha 1000.0)
pi
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))