From: Tim Bradshaw on 1 Mar 2007 06:46 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 1 Mar 2007 07:28 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 1 Mar 2007 08:46 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 1 Mar 2007 10:06 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 1 Mar 2007 12:14
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) |