Subject: Re: A style question
From: rpw3@rpw3.org (Rob Warnock)
Date: Thu, 01 Mar 2007 04:32:52 -0600
Newsgroups: comp.lang.lisp
Message-ID: <QZydndIw1N5JNnvYnZ2dnUVZ_rOqnZ2d@speakeasy.net>
lojic <lojicdotcom@gmail.com> wrote:
+---------------
| r...@rpw3.org (Rob Warnock) wrote:
| > Write the general version of DEF-FIZZ-BUZZ that accepts a
| > function name (so we can tell them apart) and an alist of primes
| > and strings, and emits similarly-correct/fast code. E.g., the
| > example we've been using all along would be generated like so:
| >
| >     (def-fizz-buzz 'fizz-buzz '((3 "Fizz") (5 "Buzz")))
| 
| That goes back to my earlier post of the following generalized
| solution:
| 
| (defun fizz-buzz (n lst)
|   (do ((i 1 (+ i 1)))
|     ((> i n))
|     (let
|       ((fizzed nil))
|       (dolist (obj lst)
|         (let ((a (car obj))
|               (str (car (cdr obj))))
|           (when (zerop (mod i a))
|             (princ str)
|             (setf fizzed t))))
|       (if (not fizzed)
|         (princ i))
|       (terpri))))
| 
| (fizz-buzz 15 '((3 "Fizz") (5 "Buzz")))
+---------------

That's fine, for a run-time function. [I had a bit of a chuckle at what
(fizz-buzz 100 '((2 "Burp")(3 "Fizz") (5 "Buzz")(7 "Bang")(11 "Boom!")))
outputs...]

+---------------
| This was my off-the-cuff approach from a non-Lisp background.
+---------------

It's actually reasonably "Lispy" as is. Oh, sure, you might use
(DOTIMES (I N) ...) or (LOOP FOR I FROM 1 TO N DO ...) instead of DO;
and you might want to use CADR or SECOND instead of (CAR (CDR ...))
and hold off on fetching that until inside the WHEN; and also the
LET is formatted uncommonly; and use UNLESS instead of (IF (NOT...)...);
and call things LIST, not LST; little stuff like that, e.g.:

  (defun fizz-buzz (n list)
    (dotimes (i n)
      (let ((fizzed nil))
        (dolist (item list)
          (when (zerop (mod i (first item)))
	    (princ (second item))
	    (setf fizzed t)))
        (unless fizzed
          (princ i))
	(terpri))))

And you could use LOOP instead of the inner DOLIST and replace the
LET with a WITH term, but then it'd probably look *less* "Lispy"
to many people:

  (defun fizz-buzz (n list)
    (dotimes (i n)
      (loop with fizzed = nil
	    for item in list
	when (zerop (mod i (first item)))
	  do (princ (second item))
	     (setf fizzed t)
	finally (unless fizzed
		  (princ i))
	        (terpri))))

+---------------
| I would like to see a macro-ized version...
+---------------

Did you see <nallen05@gmail.com>'s version for the case of a
hard-coded '((3 "Fizz") (5 "Buzz")) list?

+---------------
| ...and get some feedback from the group regarding the
| appropriateness of macros in this case vs. a simple
| function as in the above.
+---------------

A macro would fix the LIST at macroexpansion time, which allows
the macro to potentially generate better code [possibly *much*
better code, as <nallen05@gmail.com> showed], but loses the
runtime flexibility of your above function.

One hybrid that might be useful would be a function that
looked at N and the length of LIST (and all the CARs) and
decided whether to do it the straightforward way (as above)
or whether to use an initialization step to pre-bake some
auxiliary tables to speed up the computation [e.g., like
the circular lists in my version, to avoid MOD, but computed
at runtime].


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607