From: Rob Warnock on
Ken Tilton <kentilton(a)gmail.com> wrote:
+---------------
| ps. Me, I am not starting until we get the functional requirements
| cleared up. k
+---------------

See my reply to <nallen05(a)gmail.com>'s MOD-free code-generating macro,
or take this instead:

Macro DEF-FIZZ-BUZZ -- Define a "fizz-buzz"-generating function

Syntax:
def-fizz-buzz fname alist ==> fname

Arguments and Values:
fname -- A symbol, the name of the function to be defined (as by DEFUN).

alist -- an association list, whose keys are prime integers > 1
and whose values are strings.

Description:

DEF-FIZZ-BUZZ defines a function named FNAME of one argument,
a positive integer. When called with an argument (say) N, FNAME
will print each positive integer starting with 1 below N, followed
by a newline, except that if any of the keys of the ALIST evenly
divide the current integer, then the corresponding string value(s)
of the key(s) dividing the current integer will be printed instead
of the integer itself. Note: If multiple keys divide the current
integer, all of the corresponding string values will be printed,
in the same order as the elements of the ALIST. Only one copy
of any string value will be printed for any current integer.

Examples:

(def-fizz-buzz 'fizz-buzz '((3 . "Fizz") (5 . "Buzz"))) ==> FIZZ-BUZZ

(fizz-buzz 22)
>> 1
>> 2
>> Fizz
>> 4
>> Buzz
>> Fizz
>> 7
>> 8
>> Fizz
>> Buzz
>> 11
>> Fizz
>> 13
>> 14
>> FizzBuzz
>> 16
>> 17
>> Fizz
>> 19
>> Buzz
>> Fizz
>> 22
==> NIL

(def-fizz-buzz 'very-fizzy
'((2 . "Burp") (3 . "Fizz") (5 . "Buzz") (7 . "Bang")))
==> VERY-FIZZY

(very-fizzy 16)
>> 1
>> Burp
>> Fizz
>> Burp
>> Buzz
>> BurpFizz
>> Bang
>> Burp
>> Fizz
>> BurpBuzz
>> 11
>> BurpFizz
>> 13
>> BurpBang
>> FizzBuzz
>> Burp
==> NIL

There's your spec. Where's your code? ;-}


-Rob

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

From: Vassil Nikolov on

On Wed, 28 Feb 2007 19:27:41 +0000, Tim Bradshaw <tfb(a)tfeb.org> said:
| ...
| (defun fb (n)
| ((lambda (c p)
| (funcall c c p 1)
| (lambda (c p i)
| (or (> i n)
| (funcall p c p i)))
| (lambda (c p i)
| (format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"
| (mod i 3) (mod i 5) i "Fizz" "Buzz")
| (funcall c c p (1+ i))))))
| (I should point out that I don't have a CL on the system I'm writing this.)

I found just one misplaced parenthesis (the patched version is given below),
but personally I'd prefer that the top-level call is the call to FORMAT,
such as

((lambda (n) (format t "~:{~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~] ~}" (mapcar #'(lambda (i) `(,(mod i 3) ,(mod i 5) ,i fizz buzz)) (loop for i from 1 to n collect i)))) 100)

(yes, this still uses LOOP, but it is somewhat buried).

(defun fb (n)
((lambda (c p) (funcall c c p 1))
(lambda (c p i)
(or (> i n) (funcall p c p i)))
(lambda (c p i)
(format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"
(mod i 3) (mod i 5) i "Fizz" "Buzz")
(funcall c c p (1+ i)))))

---Vassil.


--
mind mate, n.
One of two persons mentally compatible with each other (cf. soul mate).
From: Vassil Nikolov on

On 28 Feb 2007 12:01:22 -0800, "Pillsy" <pillsbury(a)gmail.com> said:
| ...
| (apply #'format t "~@{ ~2@{~D ~}~^~*Fizz ~D ~*Buzz~
| ~*Fizz ~2@{~D ~} ~*Buzz ~D ~*Fizz~
| ~2@{~D ~} ~*FizzBuzz ~%~}"
| (loop
| :for i :from 1 to 100
| :collect i))

But you want it to work for arbitrarily large values of 100...

---Vassil.


--
mind mate, n.
One of two persons mentally compatible with each other (cf. soul mate).
From: Ari Johnson on
rpw3(a)rpw3.org (Rob Warnock) writes:

> <nallen05(a)gmail.com> wrote:
> +---------------
> | Rob Warnock wrote:
> | > This one is both efficient -- *no* MOD calls at all! --
> | > *and* so ugly only a parent could love it: ;-} ;-}
> | >
> | > (defun fizz-buzz (n)
> | > (loop for i from 1 to n
> | > and three-p in '#3=(nil nil t . #3#)
> | > and five-p in '#5=(nil nil nil nil t . #5#)
> | > do ...
> |
> | This is awsome, Rob ;-) But I can make one that's faster AND uglier!
> |
> | (defmacro def-fizz-buzz ()
> | (let (l)
> | (do* ((i 1 (incf i))
> | (m3p (zerop (mod i 3))
> | (zerop (mod i 3)))
> | (m5p (zerop (mod i 5))
> | (zerop (mod i 5))))
> | ((> i 15))
> | (setq l
> | (list* `(print ,(cond ((and m3p m5p) "FizzBuzz")
> | (m3p "Buzz")
> | (m5p "Fizz")
> | (t 'y)))
> | '(when (> y x) (return))
> | '(incf y)
> | l)))
> | `(defun fizz-buzz (x)
> | (let ((y 0))
> | (loop ,@(reverse l))))))
> +---------------
>
> Yes!! *That's* how to use macros to write code for you!! ;-}
>
> One minor tweak -- instead of:
>
> (INCF Y)
> (WHEN (> Y X) (RETURN))
>
> you could use:
>
> (WHEN (> (INCF Y) X) (RETURN))
>
> Another really minor tweak -- in the DO* variable bindings,
> instead of (M3P (ZEROP (MOD I 3)) (ZEROP (MOD I 3)) you can
> write (M3P #1=(ZEROP (MOD I 3)) #1#).
>
> Now for *lots* of extra credit... ;-} ;-}
>
> 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")))

Syntax is slightly different, see comments below. This uses all the
dirty tricks that I've seen discussed so far.

(setf *print-circle* t) ; You'll thank me later

(defun primep (n)
; Most naive and trusting function I've ever written:
; assume that all integers are prime
(integerp n))

(defun compose-fizz-list-item (n fizzes)
(apply #'concatenate 'string
(mapcan #'(lambda (fizz)
(when (zerop (mod n (car fizz)))
(list (cadr fizz))))
fizzes)))

(defun compose-fizz-list (lcm fizzes)
(let ((fizz-list
(loop for n from 1 to lcm
collect (compose-fizz-list-item n fizzes))))
(setf fizz-list
(mapcar #'(lambda (string) (if (string= string "") nil string))
fizz-list))
(setf (nthcdr lcm fizz-list) fizz-list)
fizz-list))

; (make-fizz fizz-buzz 100 (3 "Fizz") (5 "Buzz"))
(defmacro make-fizz (name ceiling &rest fizzes)
(loop for fizz in fizzes
when (or (not (primep (car fizz)))
(not (stringp (cadr fizz))))
do (error "syntax error"))
(let* ((lcm (apply #'lcm (mapcar #'(lambda (fizz) (car fizz)) fizzes)))
(fizz-list (compose-fizz-list lcm fizzes)))
`(defun ,name ()
; Don't care about variable capture since we don't have a body
(loop for n from 1 upto ,ceiling for fizz in ',fizz-list
collect (if fizz fizz n)))))
From: Vassil Nikolov on

On Wed, 28 Feb 2007 22:29:20 -0600, rpw3(a)rpw3.org (Rob Warnock) said:
| ...
| 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")))

But there is nothing special about primes in this problem. It is all
about having a finite number of (possibly overlapping) classes (whose
union does not necessarily cover the universe), and tagging values
with regards to membership in said classes. ("Class", of course, in
the usual set-theoretical sense, not in the more specialized OO
sense.)

Here is a sketch of the nucleus of a solution that trivially scales
into an arbitrary number of such classes, each specified by its
predicate. It uses divisibility-by-a-prime classes only to illustrate,
but obviously the arithmetic aspect is abstracted away. (For brevity,
we take some shortcuts, e.g. by "collapsing" the tags and the predicates
into the same list of symbols.)

(defun classify (x)
"Return a list of tags, one for each predicate satisfied by the argument.
If all predicates are false, a list containing only the argument itself
is returned.

Implementation note: Writing a compiler from the return value into a
FORMAT control string is left as an exercise for the reader."
(classify-aux x '(quinque tres duo) '()))

(defun classify-aux (x tags val)
;; correctness should be apparent; trivially DO-able
(if (endp tags) (or val (list x))
(classify-aux x
(rest tags)
(if (funcall (first tags) x)
(cons (first tags) val)
val))))

(defun duo (i) (zerop (mod i 2)))
(defun tres (i) (zerop (mod i 3)))
(defun quinque (i) (zerop (mod i 5)))

(classify 101) => (101)
(classify (* 2 3 5)) => (duo tres quinque)

Note: we do want to do each test exactly once for elegance, even if
the predicates are tabulated.

---Vassil.


--
mind mate, n.
One of two persons mentally compatible with each other (cf. soul mate).