From: Tim Bradshaw on
On Mar 1, 3:19 am, "lojic" <lojicdot...(a)gmail.com> wrote:

> Disturbing yet impressive. Anyone want to give a hand to the newbie in
> understanding the above?

Surely it's self evident? I mean it has hardly any parens, and
everyone knows the problem with Lisp is the parens.

From: alexshinn on
On Mar 1, 2:32 am, nalle...(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 (format t "~a~%" (cond
> > ((and three-p five-p) "FizzBuzz")
> > (three-p "Fizz")
> > (five-p "Buzz")
> > (t i)))))
>
> This is awsome, Rob ;-) But I can make one that's faster AND uglier!

But not general!

(defmacro def-fizz-buzz (name ls)
(let* ((mods (mapcar #'car ls))
(lcm (apply #'lcm mods))
(len (length ls))
(groups (make-array (expt 2 len) :initial-element '()))
(n (gensym "n"))
(count (gensym "i")))
(do ((i 0 (+ i 1)))
((>= i lcm))
(let ((group-index
(do ((x (reverse (mapcar #'(lambda (m) (mod i m)) mods))
(cdr x))
(gi 0 (+ (ash gi 1) (if (zerop (car x)) 1 0))))
((null x) gi))))
(setf (aref groups group-index) (cons i (aref groups group-
index)))))
`(defun ,name (,n)
(do ((,count 1 (+ ,count 1)))
((> ,count ,n))
(case (mod ,count ,lcm)
,@(loop for i from 1 to (- (expt 2 len) 1)
when (consp (aref groups i))
collect
`(,(reverse (aref groups i))
(format t "~A~%"
,(let ((s ""))
(loop for j from 0 to (- len 1)
for str in ls
when (not (zerop (logand i (expt 2
j))))
do (setf s (concatenate 'string s (cadr
str))))
s))))
(t (format t "~S~%" ,count)))))))


(def-fizz-buzz fizz-buzz ((3 "Fizz") (5 "Buzz") (6 "Gorp")))

=>

(defun fizz-buzz (n)
(do ((i 1 (+ i 1)))
((> i n))
(case (mod i 30)
((3 9 21 27) (format t "~A~%" "Fizz"))
((5 10 20 25) (format t "~A~%" "Buzz"))
((15) (format t "~A~%" "FizzBuzz"))
((6 12 18 24) (format t "~A~%" "FizzGorp"))
((0) (format t "~A~%" "FizzBuzzGorp"))
(t (format t "~S~%" i)))))

Note the numbers don't need to be prime, or even
relatively prime.

There are no conditionals (which are many times
slower than MOD on modern processors), except for
a CASE that can easily be jump-tabled.

Outputting the same code as nalle would be a minor
modification, though if you're going to unroll the
loop it would be better to remove the WHEN's and
have a separate block to handle the (mod n lcm)
remaining few numbers after walking the loop in steps
of lcm.

--
Alex

From: Ken Tilton on


Rob Warnock wrote:
> 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,

er, two?

> a positive integer. When called with an argument (say) N, FNAME
> will print each positive integer starting with 1

I am concerned that we are misunderstanding each other, since 1 would
never be divisible by any prime integer > 1.

> ... below N,

Did you mean "to N"? I ask only because that was the original spec and
it sounded inclusive and I have not seen anything necessitating a
change. Just want to make sure that was intended.

> 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? ;-}

Hunh? Right here:

"...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. 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."

I think most commercial Lisp compilers would accept that. if you are
stuck with a "free" Lisp, try:

(defun fbuzz (n subs)
(loop for i below n
do (print (or
(loop for (d . sub) in subs
when (zerop (mod i d))
collect sub into chat
finally
(when chat
(return (apply 'concatenate 'string chat))))
i))))

kt

--
Well, I've wrestled with reality for 35 years, Doctor, and
I'm happy to state I finally won out over it.
-- Elwood P. Dowd

In this world, you must be oh so smart or oh so pleasant.
-- Elwood's Mom
From: Ken Tilton on


Ken Tilton wrote:
>
>
> Rob Warnock wrote:
>
>> 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,
>
>
> er, two?
>
>> a positive integer. When called with an argument (say) N, FNAME
>> will print each positive integer starting with 1
>
>
> I am concerned that we are misunderstanding each other, since 1 would
> never be divisible by any prime integer > 1.
>
>> ... below N,
>
>
> Did you mean "to N"? I ask only because that was the original spec and
> it sounded inclusive and I have not seen anything necessitating a
> change. Just want to make sure that was intended.
>
>> 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? ;-}
>
>
> Hunh? Right here:
>
> "...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. 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."
>
> I think most commercial Lisp compilers would accept that. if you are
> stuck with a "free" Lisp, try:
>
> (defun fbuzz (n subs)
> (loop for i below n

oops, "from 1"

And here is the less functional version:

(defun fbuzz2 (n subs)
(loop for i from 1 below n
do (loop with printed
for (d . sub) in subs
when (zerop (mod i d))
do (setf printed t)
(princ sub)
finally (unless printed (princ i))
(terpri))))

I just hate SETF. Which reminds me:

(defmd xlater ()
xlates
(xlation (c? (bwhen (v (^value))
(loop for (d . sub) in (^xlates)
when (zerop (mod v d))
collect sub))))
(view (c? (bif (xl (^xlation))
(apply 'concatenate 'string xl)
(^value)))))

(defobserver view ((self xlater))
(when new-value (print new-value)))

Test:

(loop with xl = (make-instance 'xlater
:value (c-in nil)
:xlates '((3 . "Fizz")(5 . "Buzz")))
for n from 1 below 20
do (setf (value xl) n))

Damn setf.

kt

--
Well, I've wrestled with reality for 35 years, Doctor, and
I'm happy to state I finally won out over it.
-- Elwood P. Dowd

In this world, you must be oh so smart or oh so pleasant.
-- Elwood's Mom
From: Brian Adkins on
themba.fletcher(a)gmail.com wrote:
> OK, hello c.l.l from another noob lurker. I guess this is as good a
> way to jump in as any. It's my first macro, so please be gentle.

From one newbie to another, that's a great first macro :)

I'd still like to get some experts to weigh in on the appropriateness of
macros in this case.

For example, if we're only going to call fizzbuzz once for a given set
of primes+strings (unlikely), then I think defining a function is
sufficient:

(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")))

And if it will be called repeatedly and we don't want to pass the list
argument repeatedly, wouldn't defining a new function be sufficient?

(defun very-fizzy (n)
(fizz-buzz n '((3 "Fizz") (5 "Buzz"))))

(very-fizzy 15)

Any advantages of macros over this?

> I had to unquote the arguments to def-fizz-buzz in order to make it
> work. I'm wondering why, since macro arguments aren't evaluated, your
> spec quoted both the name and the alist?
>
> (defun new-counter (target true-value &optional (false-value ""))
> (let ((counter 0))
> #'(lambda ()
> (if (= (incf counter) target)
> (progn
> (setf counter 0)
> true-value)
> false-value))))
>
>
> (defmacro def-fizz-buzz (name condition-list)
> ;; sample call: (def-fizz-buzz fizz-buzz ((3 . "Fizz") (5 .
> "Buzz")))
> (let ((conditions (loop for e in condition-list collecting
> (list 'new-counter (car e) (cdr e)))))
> `(defun ,name (n)
> (let ((conds (list ,@conditions)))
> (loop for i from 1 to n do
> (let ((print-value (apply #'concatenate 'string
> (mapcar #'funcall conds))))
> (if (string= print-value "")
> (print i)
> (print print-value))))))))
>
>
> CL-USER> (def-fizz-buzz very-fizzy ((2 . "Burp") (3 . "Fizz") (5 .
> "Buzz") (7 . "Bang")))
>
> VERY-FIZZY
> CL-USER> (very-fizzy 16)