tayssir wrote:To be cute, I responded with:
- Code: Select all
(defmethod fib ((n (eql 0)))
(defmethod fib ((n (eql 1)))
(defmethod fib (n)
(+ (fib (- n 1))
(fib (- n 2))))
(Is this a silly trick? Well, maybe the whole example is silly -- people who actually need the Fibonacci numbers probably use a different algorithm, beyond even caching. )
IMO, that specific case is a bit of a silly trick. In other words, I wouldn't write FIB that way, even if I used that simple algorithm, mostly because you have to have all the three pieces of it to work correctly and so if they all have to be defined together, you might as well put them in a single definition with a COND form. But, the silly trick does make the point quite well.
Where I find that the GF dispatch really helpful is where I want to effectively expand a dispatch table at runtime. Say you're writing a network analyzer that decodes packets. If you want to be able to have a modular system, where you can load new functions that decode different packet types, you'd like to avoid having all the logic that figures out the packet type and then hands the packet to the decode centralized. If you do that, you either end up editing the dispatch function every time you add a new decoder, or you end up designing a dynamic dispatching scheme using something like a hash table. That last one works, and is relatively easy to do, but it's so much easier to just define a generic function and have CLOS figure it all out. And optimize it all for high performance, for that matter.
Peter Seibel used this technique to great effect in his binary decoder routines in Practical Common Lisp. I was reading through those chapters when I had that "aha!" moment.