Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A style question

134 views
Skip to first unread message

job-27...@craigslist.org

unread,
Feb 27, 2007, 8:12:33 PM2/27/07
to
A friend passed on an article regarding the difficulty job candidates
had in producing even simple programs (simple as in should take a minute
or less). One example was a program to print the numbers 1 to 100 except
that "Fizz" should be substituted for numbers divisible by 3, "Buzz"
should be substituted for numbers divisible by 5, and "FizzBuzz" should
be substituted for numbers divisible by both 3 and 5.

So, having received my "ANSI Common Lisp" and "Practical Common Lisp"
books two days ago (still waiting for "Structure and Interpretation of
Computer Programs") and having a *couple hours* of Lisp under my belt, I
cranked out a Lisp version. It seems a bit clunky, so I thought I'd see
if anyone had suggestions for improvements.

Here's a Ruby version for comparison:

def fizz_buzz n
1.upto(n) do |i|
print "Fizz" if fizz = (i % 3) == 0
print "Buzz" if buzz = (i % 5) == 0
puts fizz || buzz ? "" : i
end
end

fizz_buzz 100

and the Lisp version

(defun fizz-buzz (n)
(do ((i 1 (+ i 1)))
((> i n))
(let
((fizz (= 0 (mod i 3)))
(buzz (= 0 (mod i 5))))
(if fizz (format t "Fizz"))
(if buzz (format t "Buzz"))
(format t "~A~%"
(if (or fizz buzz) "" i)))))

(fizz-buzz 100)

By the way, it seems more folks recommend starting with "Practical
Common Lisp" instead of "ANSI Common Lisp" (when both are mentioned),
but being a newbie myself, it seems that the latter is a better
introduction to the language.

Brian

Frank Buss

unread,
Feb 27, 2007, 8:38:05 PM2/27/07
to
job-27...@craigslist.org wrote:

> I cranked out a Lisp version. It seems a bit clunky, so I thought I'd see
> if anyone had suggestions for improvements.

looks ok, but I would use loop and organize the body of the loop a bit
different:

(defun fizz-buzz (n)
(loop for i from 1 to n
for fizz = (zerop (mod i 3))
for buzz = (zerop (mod i 5)) do
(when fizz (princ "Fizz"))
(when buzz (princ "Buzz"))
(when (not (or fizz buzz)) (princ i))
(terpri)))

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

job-27...@craigslist.org

unread,
Feb 27, 2007, 9:02:46 PM2/27/07
to
Frank Buss wrote:
> job-27...@craigslist.org wrote:
>
>> I cranked out a Lisp version. It seems a bit clunky, so I thought I'd see
>> if anyone had suggestions for improvements.
>
> looks ok, but I would use loop and organize the body of the loop a bit
> different:
>
> (defun fizz-buzz (n)
> (loop for i from 1 to n
> for fizz = (zerop (mod i 3))
> for buzz = (zerop (mod i 5)) do
> (when fizz (princ "Fizz"))
> (when buzz (princ "Buzz"))
> (when (not (or fizz buzz)) (princ i))
> (terpri)))
>

Thanks. Some questions/comments:

1) In "ANSI Common Lisp", Graham makes the following comments:
"The loop macro was originally designed to help inexperienced Lisp
users write iterative code...Unfortunately, loop is more like English
than its designers ever intended...to understand it in the abstract is
almost impossible...For such reasons, the use of loop cannot be
recommended."

Is this a minority view? One of the things that attracted me to Lisp
was the simplicity, consistency, etc. of the language, so when I read
the above, it seemed reasonable.

2) Thanks for the zerop tip. I like (zerop (mod m n)) better than (= 0
(mod m n))

3) Any reason why you chose (when fizz (princ "Fizz")) instead of (if
fizz (princ "Fizz")) ?

4) Curious about the history of "terpri" - I guess it's shorter than
"newline" but not very intuitive :)

I'm really enjoying learning Lisp. I realize at this stage I still have
some "the grass is greener" and "oh cool, something new to learn!"
influences, but I expect as that wears off the merits of the language
will continue to shine through.

Brian

Lars Rune Nøstdal

unread,
Feb 27, 2007, 9:08:39 PM2/27/07
to
My first try:


(loop :for i :from 1 :upto 100
:doing (cond
((= 0 (mod i 3) (mod i 5)) (write-line "FizzBuzz"))
((= 0 (mod i 3)) (write-line "Fizz"))
((= 0 (mod i 5)) (write-line "Buzz"))
(t (format t "~A~%" i))))


..then..


(loop :for i :from 1 :upto 100
:doing
(let ((fizz-or-buzz nil))
(when (= 0 (mod i 3)) (princ "Fizz") (setf fizz-or-buzz t))
(when (= 0 (mod i 5)) (princ "Buzz") (setf fizz-or-buzz t))
(unless fizz-or-buzz (princ i))
(terpri)))


I suppose one could write a version of cond with a :when-no-clauses clause
or something to do some hiding. Maybe `loop' already has something? Well,
gonna try:


(defmacro cond* (&body body)
`(let ((any-clause-p nil))
,@(mapcar (lambda (clause-form)
(if (eq :unless-till-now (first clause-form))
`(unless any-clause-p ,(second clause-form))
`(when ,(first clause-form)
,(second clause-form)
(setf any-clause-p t))))
body)))


(loop :for i :from 1 :upto 100
:doing
(cond*
((= 0 (mod i 3)) (princ "Fizz"))
((= 0 (mod i 5)) (princ "Buzz"))
(:unless-till-now (princ i)))
(terpri))


":unless-till-now" .. not sure about the name, but I'm thinking "unless
any clause has been true up to now". So it works in the middle too.

Now, hoping that I've not made too many mistakes; is there anyone who would
like to pay me to do tasks like this? :}


--
Lars Rune Nøstdal
http://nostdal.org/

Paul Wallich

unread,
Feb 27, 2007, 9:21:26 PM2/27/07
to

In both cases, what the local variables add in supposed elegance seems
to me lost by the clunkiness of setting them in the first place (which
you haven't done correctly).

I'd probably brute-force the problem with a simple cond with a nested
conditions, along the lines of
(cond ((zerop (mod i 5))
(cond ((zerop (mod i 3)) (print "FizzBuzz"))
(t (print "Buzz"))))
((zerop (mod i 3))
(print "Fizz"))
(t (print i)))

Someone else can do the loop version, the version with a macro that
generates any possible fizz-buzz function of two small integers, the ver

Lars Rune Nøstdal

unread,
Feb 27, 2007, 9:24:23 PM2/27/07
to
On Wed, 28 Feb 2007 02:08:39 +0000, Lars Rune Nøstdal wrote:

> (defmacro cond* (&body body)
> `(let ((any-clause-p nil))


Yup; I forgot the gensym:


(defmacro cond* (&body body)
(let ((any-clause-p (gensym)))
`(let ((,any-clause-p nil))


,@(mapcar (lambda (clause-form)
(if (eq :unless-till-now (first clause-form))

`(unless ,any-clause-p ,(second clause-form))


`(when ,(first clause-form)
,(second clause-form)

(setf ,any-clause-p t))))
body))))

Wade Humeniuk

unread,
Feb 27, 2007, 9:27:48 PM2/27/07
to
Another style

(defun fbv (n)
(or (remove nil (list (and (zerop (mod n 3)) "Fizz")
(and (zerop (mod n 5)) "Buzz")))
(list n)))


(defun fizz-buzz (n)
(loop for i from 1 to n do (format t "~{~A~}~%" (fbv i))))

Wade

Pillsy

unread,
Feb 27, 2007, 9:38:28 PM2/27/07
to
On Feb 27, 9:02 pm, job-271842...@craigslist.org wrote:
[...]
> Thanks. Some questions/comments:

> 1) In "ANSI Common Lisp", Graham makes the following comments:

> "The loop macro was originally designed to help inexperienced
> Lisp users write iterative code...Unfortunately, loop is more like
> English than its designers ever intended...to understand it in the
> abstract is almost impossible...For such reasons, the use of loop
> cannot be recommended."

> Is this a minority view?

I think it is. I cordially despise LOOP myself, but almost everyone
uses it for simple stuff some of the time. You can avoid it entirely
if you want, but it can make your life unneccessarily difficult do.

You should spend some time becoming familiar with it. At the very
least, you need a basic understanding to read other people's
code. Then you can make an informed decision to avoid it. :)

> One of the things that attracted me to Lisp was the simplicity,
> consistency, etc. of the language, so when I read the above, it
> seemed reasonable.

Common Lisp is in some ways simple, and pretty consistent, but it's
also huge and not particularly "pure". Personally, I like the sense of
compromise and age.

Others prefer Scheme, which is a different dialect of Lisp which is
all about being small and elegant and pure. I find it sterile and
spare and unfun myself.

> 3) Any reason why you chose (when fizz (princ "Fizz")) instead of (if
> fizz (princ "Fizz")) ?

It's a little bit of Common Lisp style. If you are only going to use
the true branch of the conditional, use "when", and if you're only
going to use the false branch, use "unless".

Hope you continue to have fun.

Cheers,
Pillsy

Thierry Pirot

unread,
Feb 27, 2007, 9:55:45 PM2/27/07
to
job-271842874 writes:

> (defun fizz-buzz (n)
> (do ((i 1 (+ i 1)))
> ((> i n))
> (let
> ((fizz (= 0 (mod i 3)))
> (buzz (= 0 (mod i 5))))
> (if fizz (format t "Fizz"))
> (if buzz (format t "Buzz"))
> (format t "~A~%"
> (if (or fizz buzz) "" i)))))
>
> (fizz-buzz 100)
>

In a more functional style, my 2 cents,

CL-USER> (defun printing (i)
(format t "~a~%"
(cond
((= 0 (mod i 3)) "Fizz")
((= 0 (mod i 5)) "Buzz")
(t i))))

CL-USER> (defun fb (i n)
(when (< i n)
(printing i)
(fb (1+ i) n)))

CL-USER> (fb 1 100)
--
Take it Easy Don't worry Be Happy

Thierry

°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°

Wade Humeniuk

unread,
Feb 27, 2007, 10:02:30 PM2/27/07
to

And with a few macros,

;--- Part of a handy kit
(defun prinl (list)
(map nil 'princ list)
(terpri))

(defmacro over (i start end &body body)
`(loop for ,i from ,start to ,end do ,@body))
(defmacro no-nulls (&body tests)
`(remove nil (list ,@tests)))
;----------------------------------------------

(defun fbv (n)
(or (no-nulls


(and (zerop (mod n 3)) "Fizz")
(and (zerop (mod n 5)) "Buzz"))

(list n)))


(defun fizz-buzz (n)
(over i 1 n (prinl (fbv i))))

Wade

Ken Tilton

unread,
Feb 27, 2007, 10:11:06 PM2/27/07
to

I would object to using cond to implement if, and the redundant code
testing for (mod i 3) and associating it with Fizz.

Otherwise, I am bothered by the spec. Is the FizzBuzz on 15 tied to it
factoring to 3 and 5 and those substitutions, or is that coincidental?
ie, Could an RFE come thru for 15 to translate to Bang? Also, is this
list expected to expand? If so, will the test always be even divisibility?

This is how I get thrown out of most tech interviews.

kt

--
"I don't like being tested."
-- Tangina, Poltergeist

Ken Tilton

unread,
Feb 27, 2007, 10:18:23 PM2/27/07
to

I was anti-loop bigot for years, now I cannot even remember the syntax
of dolist. PG is a closet Schemer. Look at Arc. Pure minimalist. He does
not like CLOS either. Now it may be that /you/ are a latent Schemer and
that would be fine, but the spirit of Common Lisp is the more mud the
better. LOOP rocks.

>
> 2) Thanks for the zerop tip. I like (zerop (mod m n)) better than (= 0
> (mod m n))
>
> 3) Any reason why you chose (when fizz (princ "Fizz")) instead of (if
> fizz (princ "Fizz")) ?
>
> 4) Curious about the history of "terpri" - I guess it's shorter than
> "newline" but not very intuitive :)
>
> I'm really enjoying learning Lisp. I realize at this stage I still have
> some "the grass is greener" and "oh cool, something new to learn!"
> influences, but I expect as that wears off the merits of the language
> will continue to shine through.

It does not wear off:

http://wiki.alu.org/RtL_Highlight_Film

Now get to work:

http://wiki.alu.org/The_Road_to_Lisp_Survey

Anybody want to let me know about Roads they have added over the past
years that never made it to the highlight film? I mean, it /is/ a wiki,
but I would be happy to play editor and pick out the sound bites from
anyone not yet in the highlight film.

kzo

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

Thierry Pirot

unread,
Feb 27, 2007, 10:24:09 PM2/27/07
to
Thierry Pirot writes:

> job-271842874 writes:
>
> > (defun fizz-buzz (n)
> > (do ((i 1 (+ i 1)))
> > ((> i n))
> > (let
> > ((fizz (= 0 (mod i 3)))
> > (buzz (= 0 (mod i 5))))
> > (if fizz (format t "Fizz"))
> > (if buzz (format t "Buzz"))
> > (format t "~A~%"
> > (if (or fizz buzz) "" i)))))
> >
> > (fizz-buzz 100)
> >
> In a more functional style, my 2 cents,
>

Ups, it's late here...


> CL-USER> (defun printing (i)
> (format t "~a~%"
> (cond

((= 0 (mod i (* 3 5))) "FizzBuzz")

Vassil Nikolov

unread,
Feb 27, 2007, 10:23:01 PM2/27/07
to

On Tue, 27 Feb 2007 21:02:46 -0500, job-27...@craigslist.org said:
|...

| 1) In "ANSI Common Lisp", Graham makes the following comments:
| "The loop macro was originally designed to help inexperienced Lisp
| users write iterative code...Unfortunately, loop is more like English
| than its designers ever intended...to understand it in the abstract is
| almost impossible...For such reasons, the use of loop cannot be
| recommended."

| Is this a minority view? One of the things that attracted me to Lisp
| was the simplicity, consistency, etc. of the language, so when I read
| the above, it seemed reasonable.

Gain experience with Common Lisp; in particular, learn (in alphabetical
order) DO, DOLIST, DOTIMES, and LOOP. Use them and see how others have
used them. Then form your own opinion.


| ...


| 4) Curious about the history of "terpri" - I guess it's shorter than
| "newline" but not very intuitive :)

Interesting---I was checking how googlable that was, and it turns out
there is even http://terpri.org/ (q.v.).

---Vassil.

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

job-27...@craigslist.org

unread,
Feb 27, 2007, 10:30:06 PM2/27/07
to
Ken Tilton wrote:
> Paul Wallich wrote:
>> job-27...@craigslist.org wrote:
>>
>>> A friend passed on an article regarding the difficulty job candidates
>>> had in producing even simple programs (simple as in should take a
>>> minute or less). One example was a program to print the numbers 1 to
>>> 100 except that "Fizz" should be substituted for numbers divisible by
>>> 3, "Buzz" should be substituted for numbers divisible by 5, and
>>> "FizzBuzz" should be substituted for numbers divisible by both 3 and 5.
>>>
>> I'd probably brute-force the problem with a simple cond with a nested
>> conditions, along the lines of
>> (cond ((zerop (mod i 5))
>> (cond ((zerop (mod i 3)) (print "FizzBuzz"))
>> (t (print "Buzz"))))
>> ((zerop (mod i 3))
>> (print "Fizz"))
>> (t (print i)))
>
> I would object to using cond to implement if, and the redundant code
> testing for (mod i 3) and associating it with Fizz.

I agree. I expect mod is fast, but I don't like to repeat myself.

> Otherwise, I am bothered by the spec. Is the FizzBuzz on 15 tied to it
> factoring to 3 and 5 and those substitutions, or is that coincidental?
> ie, Could an RFE come thru for 15 to translate to Bang? Also, is this
> list expected to expand? If so, will the test always be even divisibility?

Yes, the FizzBuzz on 15 is because the spec said to print FizzBuzz
instead of i for any i that is divisible by both 3 and 5. Regarding
enhancements, I'll be including one in a reply to Paul in a few minutes.

Ken Tilton

unread,
Feb 27, 2007, 10:43:14 PM2/27/07
to

Sorry, I threw in that translation to 15 myself. But the question still
stands: could an RFE come along saying "if divisible by 3 and 5 print
BANG"? Or was the spec trying (and failing to say): the translations are
3 this and five that, where multiple matches occur print all. Then my
next question is: 45 is FizzFizzBuzz?

I have had managers lunge acroos the table to get at me.

kt

job-27...@craigslist.org

unread,
Feb 27, 2007, 10:45:41 PM2/27/07
to
Paul Wallich wrote:
> job-27...@craigslist.org wrote:
>> (defun fizz-buzz (n)
>> (do ((i 1 (+ i 1)))
>> ((> i n))
>> (let
>> ((fizz (= 0 (mod i 3)))
>> (buzz (= 0 (mod i 5))))
>> (if fizz (format t "Fizz"))
>> (if buzz (format t "Buzz"))
>> (format t "~A~%"
>> (if (or fizz buzz) "" i)))))
>
> In both cases, what the local variables add in supposed elegance seems
> to me lost by the clunkiness of setting them in the first place (which
> you haven't done correctly).

Can you elaborate about how I incorrectly set the local variables? The
program functions correctly, so maybe you're referring to using zerop
instead?

> I'd probably brute-force the problem with a simple cond with a nested
> conditions, along the lines of
> (cond ((zerop (mod i 5))
> (cond ((zerop (mod i 3)) (print "FizzBuzz"))
> (t (print "Buzz"))))
> ((zerop (mod i 3))
> (print "Fizz"))
> (t (print i)))
>
> Someone else can do the loop version, the version with a macro that
> generates any possible fizz-buzz function of two small integers, the ver

I haven't made it to the chapter on macros yet, but would it be better
Lisp style to write a macro to be able to handle any two integers, or
just add 2 parameters e.g. (defun fizz-buzz (n a b) ... ), or maybe it
would be more Lisp-like to pass a list as the 2nd param (defun fizz-buzz
(n lst) ... ) where each element in the list would contain an integer
and the replacement string - something like:

(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 100 '((3 "Fizz") (5 "Buzz")))

I know this is butt ugly, but the principle of generalizing with a 2nd
list parameter seems like a reasonable enhancement. Since I'm not to the
point of "thinking in Lisp", this would be my first approach, but maybe
macros would be the way to go here. Hopefully, I'll have a feel for that
after chapter 10 :)

Brian


Vassil Nikolov

unread,
Feb 27, 2007, 10:46:24 PM2/27/07
to

On Tue, 27 Feb 2007 22:30:06 -0500, job-27...@craigslist.org said:

| Ken Tilton wrote:
| ...


|| I would object to using cond to implement if, and the redundant code
|| testing for (mod i 3) and associating it with Fizz.

| I agree. I expect mod is fast, but I don't like to repeat myself.

But this isn't about the speed of MOD (after all, we can justifiably
expect the compiler to arrange for the respective call to be made
no more than once), but---to reuse the expression---about getting
thrown out of the interview because of introducing the DIVISIBLE-BY-3,
DIVISIBLE-BY-5, and ANY-NUMBER classes, and doing it with method
combination to address these concerns:

|| Otherwise, I am bothered by the spec. Is the FizzBuzz on 15 tied to it
|| factoring to 3 and 5 and those substitutions, or is that coincidental?
|| ie, Could an RFE come thru for 15 to translate to Bang? Also, is this
|| list expected to expand? If so, will the test always be even divisibility?

|| This is how I get thrown out of most tech interviews.

---Vassil.

job-27...@craigslist.org

unread,
Feb 27, 2007, 11:01:02 PM2/27/07
to
Ken Tilton wrote:
> job-27...@craigslist.org wrote:
>> Ken Tilton wrote:
>>> Paul Wallich wrote:
>>>> job-27...@craigslist.org wrote:
>>>>> A friend passed on an article regarding the difficulty job
>>>>> candidates had in producing even simple programs (simple as in
>>>>> should take a minute or less). One example was a program to print
>>>>> the numbers 1 to 100 except that "Fizz" should be substituted for
>>>>> numbers divisible by 3, "Buzz" should be substituted for numbers
>>>>> divisible by 5, and "FizzBuzz" should be substituted for numbers
>>>>> divisible by both 3 and 5.
>>>>>
>>> Otherwise, I am bothered by the spec. Is the FizzBuzz on 15 tied to
>>> it factoring to 3 and 5 and those substitutions, or is that
>>> coincidental? ie, Could an RFE come thru for 15 to translate to Bang?
>>> Also, is this list expected to expand? If so, will the test always be
>>> even divisibility?
>>
>> Yes, the FizzBuzz on 15 is because the spec said to print FizzBuzz
>> instead of i for any i that is divisible by both 3 and 5. Regarding
>> enhancements,
>
> Sorry, I threw in that translation to 15 myself. But the question still
> stands: could an RFE come along saying "if divisible by 3 and 5 print
> BANG"? Or was the spec trying (and failing to say): the translations are
> 3 this and five that, where multiple matches occur print all. Then my
> next question is: 45 is FizzFizzBuzz?

Gotcha. Well, I just posted a reply to Paul with a generalization of
passing in a 2nd list parameter containing pairs of (integer, string)
with the intent of concatenating the strings for all integers that
evenly divide i.

So for the list '((2 "a")(3 "b")(5 "c")) we'd have
1
a
b
ac
5
ab
7
ac
b
a
11
abc

So, 45 would just be "FizzBuzz"

job-27...@craigslist.org

unread,
Feb 27, 2007, 11:03:29 PM2/27/07
to

Maybe I'm reading this incorrectly, but wouldn't that always print i?
Printing i is mutually exclusive with printing "Fizz" and/or "Buzz".

justinhj

unread,
Feb 27, 2007, 11:06:55 PM2/27/07
to
(defun multiple(x n)
(= 0 (mod x n)))

(defun output-multiple(x n str)
(if (and (multiple x n) (princ str))
1
0))

(defun fizzbuzz(n)
(loop for x from 1 to n do
(if (> (+ (output-multiple x 3 "fizz") (output-multiple x 5
"buzz")) 0)
(format t "~%"))))

job-27...@craigslist.org

unread,
Feb 27, 2007, 11:27:50 PM2/27/07
to
job-27...@craigslist.org wrote:
> Ken Tilton wrote:
> Gotcha. Well, I just posted a reply to Paul with a generalization of
> passing in a 2nd list parameter containing pairs of (integer, string)
> with the intent of concatenating the strings for all integers that
> evenly divide i.
>

Oops, I guess it's getting late. The output doesn't match the list - I
mistakenly used the following instead when I ran the program:
(fizz-buzz 12 '((2 "a") (3 "b") (4 "c")))
that's why 4 is ac and 12 is abc ...

job-27...@craigslist.org

unread,
Feb 27, 2007, 11:31:13 PM2/27/07
to

Hmm.. this doesn't seem to work.

[1]> (defun multiple(x n)


(= 0 (mod x n)))

MULTIPLE
[2]>


(defun output-multiple(x n str)
(if (and (multiple x n) (princ str))
1
0))

OUTPUT-MULTIPLE
[3]>


(defun fizzbuzz(n)
(loop for x from 1 to n do
(if (> (+ (output-multiple x 3 "fizz") (output-multiple x 5
"buzz")) 0)
(format t "~%"))))

FIZZBUZZ
[4]> (fizzbuzz 12)
fizz
buzz
fizz
fizz
buzz
fizz
NIL
[5]>

Dan Bensen

unread,
Feb 27, 2007, 11:42:10 PM2/27/07
to
Paul Wallich wrote:
> In both cases, what the local variables add in supposed elegance seems
> to me lost by the clunkiness of setting them in the first place
> I'd probably brute-force the problem with a simple cond

You can eliminate variables and repetition by making the string
do double duty:

(let ((str (concatenate 'string
""
(if (zerop (mod n 3)) "fizz" "")
(if (zerop (mod n 5)) "buzz" ""))))
(write-line
(if (string= str "") (format nil "~D" n) str)))

--
Dan
www.prairienet.org/~dsb

GP lisper

unread,
Feb 27, 2007, 11:14:22 PM2/27/07
to
On 27 Feb 2007 18:38:28 -0800, <pill...@gmail.com> wrote:
> On Feb 27, 9:02 pm, job-271842...@craigslist.org wrote:
>
>> "The loop macro was originally designed to help inexperienced
>> Lisp users write iterative code...Unfortunately, loop is more like
>> ...

>
>> Is this a minority view?
>
> I think it is. I cordially despise LOOP myself, but almost everyone
> uses it...

>
> You should spend some time becoming familiar with it.

When I needed to learn loop, I found that by reading the chapter in
Common Lisp, the Language I gained the best understanding.

Loop is a Domain Specific Language, something that many loop haters
overlook, as they will also cheer DSLs as a Lisp strength.

--
There are no average Common Lisp programmers
Reply-To: email is ignored.

--
Posted via a free Usenet account from http://www.teranews.com

job-27...@craigslist.org

unread,
Feb 28, 2007, 1:09:47 AM2/28/07
to
job-27...@craigslist.org wrote:
> def fizz_buzz n
> 1.upto(n) do |i|
> print "Fizz" if fizz = (i % 3) == 0
> print "Buzz" if buzz = (i % 5) == 0
> puts fizz || buzz ? "" : i
> end
> end
>
> fizz_buzz 100
>
> (defun fizz-buzz (n)
> (do ((i 1 (+ i 1)))
> ((> i n))
> (let
> ((fizz (= 0 (mod i 3)))
> (buzz (= 0 (mod i 5))))
> (if fizz (format t "Fizz"))
> (if buzz (format t "Buzz"))
> (format t "~A~%"
> (if (or fizz buzz) "" i)))))
>
> (fizz-buzz 100)

Another friend of mine commenting on the same FizzBuzz thread supplied
the following Python code. It certainly is concise:

for i in xrange(1,101):
print(str(i), "Fizz", "Buzz", "FizzBuzz")[(i%3==0)|(i%5==0)<<1]

I thought about retrofitting my Ruby version as an exercise, but alas,
Ruby doesn't allow shifting truth to the left :)

Forgive my ignorance, but is anything like the boolean bit shifting
technique used in the Python code above possible in Lisp? No big loss if
it isn't, just curious.

I suppose it's unreasonable to expect the Lisp version to be as concise
as the Python version - not only is this a toy example, but I think a
language with more syntax will be able to provide more brevity in
certain situations. That's a tradeoff I'm willing to accept given the
benefits of a syntax that's more readily parsed and manipulated.

Brian

Ken Tilton

unread,
Feb 28, 2007, 1:30:42 AM2/28/07
to

But this is a programming disaster, stupid pet trick atop stupid pet
trick. It collapses in a heap the minute anything changes. It builds
into itself all sorts of things that just happen to be true. You have
missed that what I am driving at is good programming, you are caught up
in cleverness, the road to hell in programming.

Let me help you. If, as it seems, the spec is that FizzBuzz is not
accidentally Fizz and Buzz together, then the strings Fizz and Buzz must
appear only once in the program, as must the tests (mod x 3) and (mod x 5).

The punch line is that no good programmer could write anything in five
minutes, unless the instructions included: "Just frickin make these
results appear from this input."

Rob Warnock

unread,
Feb 28, 2007, 1:31:05 AM2/28/07
to
<job-27...@craigslist.org> wrote:
+---------------

| Another friend of mine commenting on the same FizzBuzz thread supplied
| the following Python code. It certainly is concise:
|
| for i in xrange(1,101):
| print(str(i), "Fizz", "Buzz", "FizzBuzz")[(i%3==0)|(i%5==0)<<1]
|
| I thought about retrofitting my Ruby version as an exercise, but alas,
| Ruby doesn't allow shifting truth to the left :)
|
| Forgive my ignorance, but is anything like the boolean bit shifting
| technique used in the Python code above possible in Lisp? No big loss if
| it isn't, just curious.
+---------------

Well, sort of... ;-} ;-}

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


-Rob

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

Ken Tilton

unread,
Feb 28, 2007, 1:56:17 AM2/28/07
to

Yer a sick puppy, Rob, but I like it.

kt

Brian Adkins

unread,
Feb 28, 2007, 2:24:19 AM2/28/07
to
Ken Tilton wrote:
> job-27...@craigslist.org wrote:
>> job-27...@craigslist.org wrote:
>> Another friend of mine commenting on the same FizzBuzz thread supplied
>> the following Python code. It certainly is concise:
>>
>> for i in xrange(1,101):
>> print(str(i), "Fizz", "Buzz", "FizzBuzz")[(i%3==0)|(i%5==0)<<1]
>
> But this is a programming disaster, stupid pet trick atop stupid pet
> trick. It collapses in a heap the minute anything changes. It builds
> into itself all sorts of things that just happen to be true. You have
> missed that what I am driving at is good programming, you are caught up
> in cleverness, the road to hell in programming.

Agreed. Don't read too much into "It certainly is concise." My statement
was intentionally terse.

In the context of the "FizzBuzz" toy example, it can be fun to look at
some clever code, but don't infer that I'm "caught up in cleverness". In
fact, I've counseled countless coders to comprehend the concealed costs
of clever code. *ducks*

> Let me help you. If, as it seems, the spec is that FizzBuzz is not
> accidentally Fizz and Buzz together, then the strings Fizz and Buzz must
> appear only once in the program, as must the tests (mod x 3) and (mod x 5).

I feel the same way; I guess my original Lisp hack wasn't so terrible -
a couple 'o mods and a couple 'o strings. Incorporating some of the
feedback from the group gives the following:

(defun fizz-buzz (n)
(do ((i 1 (+ i 1))) ((> i n))
(let

((fizz (zerop (mod i 3)))
(buzz (zerop (mod i 5))))


(when fizz (princ "Fizz"))

(when buzz (princ "Buzz"))


(format t "~A~%" (if (or fizz buzz) "" i)))))

(fizz-buzz 100)

Now on to chapter 3 "Lists" :)

job-27...@craigslist.org

unread,
Feb 28, 2007, 2:33:08 AM2/28/07
to

Yeah, I know I should just turn away, but I can't help myself :)

I'll have to file this away and come back to it when I've learned enough
to understand it. Looks like read macros are involved, but I don't want
to get ahead of myself...

>
> kt

Rob Warnock

unread,
Feb 28, 2007, 2:46:09 AM2/28/07
to
<job-27...@craigslist.org> wrote:
+---------------

| I'll have to file this away and come back to it when I've
| learned enough to understand it. Looks like read macros
| are involved, but I don't want to get ahead of myself...
+---------------

When you're ready, see:

http://www.lisp.org/HyperSpec/Body/sec_2-4-8-15.html
2.4.8.15 Sharpsign Equal-Sign

http://www.lisp.org/HyperSpec/Body/sec_2-4-8-16.html
2.4.8.16 Sharpsign Sharpsign

Just remember to (SETF *PRINT-CIRCLE* T) before trying
experiments at the REPL...


-Rob

p.s. The fact that I used #3= and #5= carried no semantics;
it was just for documentation. They could have been any two
distinct non-zero positive integers.

Joel Wilsson

unread,
Feb 28, 2007, 3:17:00 AM2/28/07
to
On Feb 28, 7:09 am, job-271842...@craigslist.org wrote:
> Another friend of mine commenting on the same FizzBuzz thread supplied
> the following Python code. It certainly is concise:
>
> for i in xrange(1,101):
> print(str(i), "Fizz", "Buzz", "FizzBuzz")[(i%3==0)|(i%5==0)<<1]
>
> I thought about retrofitting my Ruby version as an exercise, but alas,
> Ruby doesn't allow shifting truth to the left :)

While it's "nice" in this case, I think it's a sign of a weak type
system.
Even Java has a boolean type.

> Forgive my ignorance, but is anything like the boolean bit shifting
> technique used in the Python code above possible in Lisp? No big loss if
> it isn't, just curious.

No, but of course you could easily make it.
(flet ((num-zerop (x)
(if (zerop x)
1
0)))
(defun fizz-buzz (n)
(dotimes (i (1+ n))
(format t "~A~%" (nth (logior (num-zerop (mod i 3))
(ash (num-zerop (mod i 5)) 1))
(list i "Fizz" "Buzz" "FizzBuzz"))))))

> I suppose it's unreasonable to expect the Lisp version to be as concise
> as the Python version

Due to the nature of the problem I don't think any language where
equality
testing returns true booleans can be shorter than that Python version.
This is closer to Common Lisp style I think, and slightly more
verbose:

(defun fizz-buzz (n)
(dotimes (i (1+ n))
(format t "~A~%" (nth (+ (if (zerop (mod i 3)) 1 0)
(if (zerop (mod i 5)) 2 0))
(list i "Fizz" "Buzz" "FizzBuzz")))))

In my opinion some of the first versions posted in this thread were
the
best, since they were much easier to read and understand. We're just
doing obfuscated stuff now, might as well go all out:

(defun fizz-buzz (n)
(format t "~{~A~%~}" (reduce (lambda (h r)
(cons (nth (reduce (lambda (x y)
(if (zerop (mod
h (1+ (* 2 y))))
(+ y x)
x))
'(1 2) :initial-
value 0)
(list h "Fizz" "Buzz"
"FizzBuzz")) r))
(reduce (lambda (x y)
(cons (decf n) y))
(make-list (incf n)) :from-end
t)
:from-end t :initial-value nil)))


As for your question about loop, you really should learn it. Even if
you don't use it much yourself, plenty of code written by others does.

Regards,
Joel

Rainer Joswig

unread,
Feb 28, 2007, 3:24:14 AM2/28/07
to
In article <p6aFh.35598$p9.1...@bignews7.bellsouth.net>,
Brian Adkins <br...@lojic.com> wrote:

In DO you can have more than one clauses for iteration.
So you could get rid of the extra LET.

Tim Bradshaw

unread,
Feb 28, 2007, 5:33:33 AM2/28/07
to
On Feb 28, 2:02 am, job-271842...@craigslist.org wrote:

>
> 1) In "ANSI Common Lisp", Graham makes the following comments:

> "The loop macro was originally designed to help inexperienced Lisp

> users write iterative code...Unfortunately, loop is more like English
> than its designers ever intended...to understand it in the abstract is
> almost impossible...For such reasons, the use of loop cannot be
> recommended."

Reading Paul Graham is a bit like reading reviews of films by a good
critic: he is almost always wrong about everything, but has
interesting things to say and it's possible to reliably predict
whether you'll like something from what he says about it (though often
you will differ from him on whether you like it, due to the above-
mentioned almost-always-being-wrong thing). He's kind of the Barry
Norman of Lisp, really.

>
> Is this a minority view? One of the things that attracted me to Lisp
> was the simplicity, consistency, etc. of the language, so when I read
> the above, it seemed reasonable.

Simplicity? consistency? I think you're thinking of some other
language there. CL is this vast industrial thing full of enormous
machines, oil and rust. Some compartments are full of water, and no
one knows what some of the machines do, if anything. Many parts of it
use a mixture of Whitworth & BSF threads (some left-handed), though
much has now been converted to BA or metric, sometimes by use of taps
& dies, sometimes with a hammer.

CL's closest living relative is FORTRAN: always remember that.

Incidentally, I'm deeply disappointed in the quality of answers in
this thread. In the elder days there would have been at least a few
followups showing how to do this in the proper "FORMAT string
indistinguishable from line noise" way. No true CL programmer ever
uses any other construct when the problem can be solved with a
combination of FORMAT, LOOP & GO (FORMAT being always preferable,
obviously). There may yet be those reading cll who know this, though
I suspect they have all gone into the west now.

--tim (who has used the FORMAT string mentioned in
http://www.tfeb.org/lisp/obscurities.html in anger)

John Thingstad

unread,
Feb 28, 2007, 5:40:20 AM2/28/07
to
On Wed, 28 Feb 2007 03:02:46 +0100, <job-27...@craigslist.org> wrote:

> Frank Buss wrote:
>> job-27...@craigslist.org wrote:
>>
>>> I cranked out a Lisp version. It seems a bit clunky, so I thought I'd
>>> see if anyone had suggestions for improvements.
>> looks ok, but I would use loop and organize the body of the loop a bit
>> different:

>> (defun fizz-buzz (n)
>> (loop for i from 1 to n

>> for fizz = (zerop (mod i 3))
>> for buzz = (zerop (mod i 5)) do

>> (when fizz (princ "Fizz"))
>> (when buzz (princ "Buzz"))

>> (when (not (or fizz buzz)) (princ i))
>> (terpri)))
>>
>
> Thanks. Some questions/comments:
>

> 1) In "ANSI Common Lisp", Graham makes the following comments:
> "The loop macro was originally designed to help inexperienced Lisp
> users write iterative code...Unfortunately, loop is more like English
> than its designers ever intended...to understand it in the abstract is
> almost impossible...For such reasons, the use of loop cannot be
> recommended."
>

You will find the same recommendation from Peter Norwig in "Paradigms of
AI programming".
Something along the line: 'loop has a syntax that looks like English,
unfortunately
it is nothing like English.

This is true.
For many years most lispers avoided loop. (except simplified loop)

I think the description in the ANSI CL standard entry on 'loop is next to
unreadable. This is probably the real reason it wasn't used.
What you need to learn 'loop is tons of examples.

Then along came Peter Seibels book "Practical Common Lisp"
in particular the chapter "Loop for black belts".
I know it taught me 'loop as it probably did most of the people here.
Yes 'loop's syntax is not intuitive (though highly readable).
In my opinion it's power makes it worth while to learn.

Whether you use it or not is a matter of taste.
There are other alternatives.
There is always 'do or there is a iterator package.

--
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/

Rainer Joswig

unread,
Feb 28, 2007, 5:53:23 AM2/28/07
to
In article <1172658813.7...@p10g2000cwp.googlegroups.com>,
"Tim Bradshaw" <tfb+g...@tfeb.org> wrote:

> On Feb 28, 2:02 am, job-271842...@craigslist.org wrote:
>
> >
> > 1) In "ANSI Common Lisp", Graham makes the following comments:
> > "The loop macro was originally designed to help inexperienced Lisp
> > users write iterative code...Unfortunately, loop is more like English
> > than its designers ever intended...to understand it in the abstract is
> > almost impossible...For such reasons, the use of loop cannot be
> > recommended."
>
> Reading Paul Graham is a bit like reading reviews of films by a good
> critic: he is almost always wrong about everything, but has
> interesting things to say and it's possible to reliably predict
> whether you'll like something from what he says about it (though often
> you will differ from him on whether you like it, due to the above-
> mentioned almost-always-being-wrong thing). He's kind of the Barry
> Norman of Lisp, really.

I agree. ;-)

>
> >
> > Is this a minority view? One of the things that attracted me to Lisp
> > was the simplicity, consistency, etc. of the language, so when I read
> > the above, it seemed reasonable.
>
> Simplicity? consistency? I think you're thinking of some other
> language there. CL is this vast industrial thing full of enormous
> machines, oil and rust. Some compartments are full of water, and no
> one knows what some of the machines do, if anything. Many parts of it
> use a mixture of Whitworth & BSF threads (some left-handed), though
> much has now been converted to BA or metric, sometimes by use of taps
> & dies, sometimes with a hammer.
>
> CL's closest living relative is FORTRAN: always remember that.

Common Lisp is a bit of the alligator in the pond, eating all
the younger designs. ;-)

> Incidentally, I'm deeply disappointed in the quality of answers in
> this thread. In the elder days there would have been at least a few
> followups showing how to do this in the proper "FORMAT string
> indistinguishable from line noise" way. No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.

It is always a shock to me when I look at such code. I mean
many pages long functions full of GOs, two letter variables
and such (and zero comments). I cannot
believe that humans can write this code. I always
think the author was some ugly 'Terminator' from the future,
though lately the Terminators seem to be blond and good looking.

John Thingstad

unread,
Feb 28, 2007, 6:26:18 AM2/28/07
to
On Wed, 28 Feb 2007 04:30:06 +0100, <job-27...@craigslist.org> wrote:

> Ken Tilton wrote:
>> Paul Wallich wrote:
>>> job-27...@craigslist.org wrote:
>>>
>>>> A friend passed on an article regarding the difficulty job candidates
>>>> had in producing even simple programs (simple as in should take a
>>>> minute or less). One example was a program to print the numbers 1 to
>>>> 100 except that "Fizz" should be substituted for numbers divisible by
>>>> 3, "Buzz" should be substituted for numbers divisible by 5, and
>>>> "FizzBuzz" should be substituted for numbers divisible by both 3 and
>>>> 5.
>>>>
>>> I'd probably brute-force the problem with a simple cond with a nested
>>> conditions, along the lines of
>>> (cond ((zerop (mod i 5))
>>> (cond ((zerop (mod i 3)) (print "FizzBuzz"))
>>> (t (print "Buzz"))))
>>> ((zerop (mod i 3))
>>> (print "Fizz"))
>>> (t (print i)))
>> I would object to using cond to implement if, and the redundant code
>> testing for (mod i 3) and associating it with Fizz.
>
> I agree. I expect mod is fast, but I don't like to repeat myself.
>

rem is fast.. mod conses on fractions on the heap

Pillsy

unread,
Feb 28, 2007, 7:15:13 AM2/28/07
to
On Feb 27, 11:14 pm, GP lisper <spamb...@CloudDancer.com> wrote:
[...]

> Loop is a Domain Specific Language, something that many loop haters
> overlook, as they will also cheer DSLs as a Lisp strength.

Oh, I know it's a DSL. I just think it's a crappy DSL. ITERATE is the
same sort of DSL, but is much better---easier for editors to indent
right, Lispy looking, and straightforward to extend with macros.

Cheers,
Pillsy

Thierry Pirot

unread,
Feb 28, 2007, 7:58:17 AM2/28/07
to
job-271842874writes:
> Thierry Pirot wrote:

> >> CL-USER> (defun printing (i)
> >> (format t "~a~%"
> >> (cond
> > ((= 0 (mod i (* 3 5))) "FizzBuzz")
> >> ((= 0 (mod i 3)) "Fizz")
> >> ((= 0 (mod i 5)) "Buzz")
> >> (t i))))

> Maybe I'm reading this incorrectly, but wouldn't that always print i?
>

Let's run
CL-USER> (printing 3)
Fizz
NIL
CL-USER> (printing 4)
4
NIL
CL-USER> (printing 5)
Buzz
NIL
CL-USER> (printing 30)
FizzBuzz
NIL

Let's read
http://www.lispworks.com/documentation/HyperSpec/Body/m_cond.htm
cond, a root of Lisp, is not a function,
it sequentially evaluates its clauses' antecedent until one is true and then
returns the evaluation of the consequent.
t in the last clause can be thought of as an "otherwise".
--
Take it Easy Don't worry Be Happy

Thierry

°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°şo§oş°¨¨¨¨°

Ken Tilton

unread,
Feb 28, 2007, 8:03:07 AM2/28/07
to

Oh, absolutely, long overdue in this thread. Is this going to become a
lost art? The village elders need to step up, methinks. I started
playing with it, but I am just an elder, not a Lisp elder. Screams for a
nested thingy, yes?

> No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.

Well, I did not want to get morbid, but that is what I was thinking. As
Lisp reaches fifty we can expect to see its legends start scrolling off
the top of the screen.

Ken Tilton

unread,
Feb 28, 2007, 8:06:42 AM2/28/07
to

You refer of course to the Cello code to create a 3-D oblong button of
variable thickness with rounded corners of variable radius:

(defun ix-render-oblong (lbox thickness baser slices stacks)
(unless slices (setq slices 0))
(unless stacks (setq stacks (if (zerop thickness)
0 (min 10
(max 1 ;; force 3d if nonzero
thickness
(round (abs thickness) 2))))))
(when (eql (abs thickness) (abs baser))
(setf thickness (* .99 thickness)))
(trc nil "oblong" baser thickness etages)

(loop
with theta = (/ pi 2 slices)
with etages = stacks ;; french floors (etages) zero = ground floor
with lw/2 = (/ (r-width lbox) 2)
with lh/2 = (/ (r-height lbox) 2)
with bx = (- lw/2 baser)
with by = (- lh/2 baser)
for etage upto etages
for oe = 0 then ie
for ie = (unless (= etage etages)
(* (/ (1+ etage) etages)
(/ pi 2)))
for ii = (if (not ie)
0 ;; throwaway value to avoid forever testing if nil
(+ (* (abs thickness)
(- 1 (cos ie)))))

for ox = lw/2 then ix
for oy = lh/2 then iy
for oz = 0 then iz
for oc = (cornering baser slices) then ic
for ic = (when ie
(cornering (- baser ii) slices))
for ix = (- lw/2 ii)
for iy = (- lh/2 ii)
for iz = (when ie
(* thickness (sin ie)))

do (trc nil "staging" etage ie)


(gl-translatef (+ (r-left lbox) lw/2)(+ (r-bottom lbox) lh/2) 0)

(with-gl-begun ((if ie
gl_quad_strip
gl_polygon))

(loop for (dx dy no-turn-p)
in '((1 1)(-1 1)(-1 -1)(1 -1)(1 1 t))
;;for dbg = (and (eql dx 1)(eql dy 1)(not no-turn-p))
do (destructuring-bind (xyn0 ix0 iy0 ox0 oy0)
(cons (+ (if oc (/ theta 2) 0)
(ecase dx (1 (ecase dy (1 0)(-1 (/ pi -2))))
(-1 (ecase dy (1 (/ pi 2))(-1 pi)))))
(if oc
(case (* dx dy)
(1 (list (* dx ix)(* dy by)(* dx ox)(* dy by)))
(-1 (list (* dx bx)(* dy iy)(* dx bx)(* dy
oy))))
(list (* dx ix)(* dy iy)(* dx ox)(* dy oy))))

;; --- lay-down start/only -------------
(when ie
(ogl-vertex-normaling ie xyn0 ix0 iy0 iz))
(ogl-vertex-normaling oe xyn0 ox0 oy0 oz)

(trc nil "cornering!!!!!!----------------" dx dy)
;; --- corner if slices and not just finishing strip

(unless no-turn-p
(trc nil "------ start ------------------" (length
oc)(length ic))
(loop for (oxn . oyn) in oc
for icrem = ic then (cdr icrem)
for (ixn . iyn) = (car icrem)
for xyn upfrom (+ xyn0 theta) by theta
do (macrolet
((vtx (elev gx sx gy sy gz)
`(progn
(when (minusp (* dx dy))
(rotatef ,sx ,sy))
(ogl-vertex-normaling ,elev xyn
(incf ,gx (* dx ,sx))
(incf ,gy (* dy ,sy))
,gz))))
(trc nil "ocn icn" oxn oyn (car icrem))
(when icrem
(vtx ie ix0 ixn iy0 iyn iz))
(vtx oe ox0 oxn oy0 oyn oz)))))))
(gl-translatef (- (+ (r-left lbox) lw/2))
(- (+ (r-bottom lbox) lh/2)) 0)))

> I always
> think the author was some ugly 'Terminator' from the future,
> though lately the Terminators seem to be blond and good looking.

Actually I was doing a rare transcription from a paper solution (where
short variables saved pencil lead).

Rainer Joswig

unread,
Feb 28, 2007, 8:48:27 AM2/28/07
to
In article <C7fFh.6$M2...@newsfe12.lga>,
Ken Tilton <kent...@gmail.com> wrote:

> Rainer Joswig wrote:
> > In article <1172658813.7...@p10g2000cwp.googlegroups.com>,
> > "Tim Bradshaw" <tfb+g...@tfeb.org> wrote:
>
> >>Incidentally, I'm deeply disappointed in the quality of answers in
> >>this thread. In the elder days there would have been at least a few
> >>followups showing how to do this in the proper "FORMAT string
> >>indistinguishable from line noise" way. No true CL programmer ever
> >>uses any other construct when the problem can be solved with a
> >>combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> >>obviously). There may yet be those reading cll who know this, though
> >>I suspect they have all gone into the west now.
> >
> >
> > It is always a shock to me when I look at such code. I mean
> > many pages long functions full of GOs, two letter variables
> > and such (and zero comments). I cannot
> > believe that humans can write this code.
>
> You refer of course to the Cello code to create a 3-D oblong button of
> variable thickness with rounded corners of variable radius:

This code is harmless. It is not even UPPERCASE. Not enough global variables. Scroll down!

Take this (not written by me). Well, actually there are too many comments.
Though I'm not sure if they actually would help to understand the code...


(DEFUN INCREMENTAL-SEARCH (REVERSE-P)
(INITIALIZE-INCREMENTAL-SEARCH-GLOBALS)
(SELECT-WINDOW *WINDOW*) ;Flush typeout before TYPEIN-LINE-ACTIVATE
(TYPEIN-LINE "") ;Necessary if in the mini-buffer
(UNWIND-PROTECT
(TYPEIN-LINE-ACTIVATE
(SI:WITH-STACK-ARRAY
;; Allocate an skip-table on the stack to avoid consing too much.
;; We don't bother with the reoccurrence table because (1) it's size
;; changes for each pattern string, and (2) it's small anyway.
(SKIP-RESOURCE (HIGHEST-LEGAL-CHAR-CODE) :TYPE 'ART-FIXNUM)
(PROG (CHAR ; The current command.
REAL-CHAR ; The one to :UNTYI if need be
XCHAR ; Upcase version of character
MUST-REDIS ; T => The echo-area must be completely redisplayed.
(P 0) ; The stack pointer into *IS-BP*, etc. for input and rubout
(P1 0) ; The pointer for which search we are doing.
; Can never exceed P.
SUPPRESSED-REDISPLAY ; T if the last input char was read before
; redisplay had a chance to finish.
; A G read that way acts like a failing search quit.
(BP (POINT)) ; The POINT.
BP1 ; Aux BP used for actual searching.
NEW-BP
TIME-OUT ; Set by SEARCH when it times out so we can check input.
INPUT-DONE ; An altmode or control char has been seen.
; Do not look for input any more; just search, then exit.
(ORIG-PT) ; Original position of POINT.
(SKIP-TABLE NIL)
(OLD-SKIP-TABLE NIL)
(REOCCURRENCE-TABLE NIL)
(OLD-REOCCURRENCE-TABLE NIL)
)

(SETQ ORIG-PT (COPY-BP BP))
(SETQ BP1 (COPY-BP BP)) ; This is reused to save consing.
(STORE-ARRAY-LEADER 0 *IS-STRING* 0); Clear out the search string.
(ASET T *IS-STATUS* 0) ; Initialize the stacks.
(ASET REVERSE-P *IS-REVERSE-P* 0)
(ASET ':NORMAL *IS-OPERATION* 0)
(ASET 0 *IS-POINTER* 0)
(ASET (COPY-BP BP) *IS-BP* 0)
(SETQ MUST-REDIS T) ; Initially we must redisplay.
(GO CHECK-FOR-INPUT)

;; Come here if there is input, or nothing to do until there is input.
INPUT
(SETQ SUPPRESSED-REDISPLAY NIL)
(AND (WINDOW-READY-P *WINDOW*) ;In case of minibuffer
(REDISPLAY *WINDOW* ':POINT)) ; Redisplay point position while waiting.
(OR (= (WINDOW-REDISPLAY-DEGREE *WINDOW*) DIS-NONE)
(SETQ SUPPRESSED-REDISPLAY T))
(MULTIPLE-VALUE (CHAR REAL-CHAR)
(EDITOR-INPUT :SCROLL T :MOUSE :RETURN
:ANY-TYI 'COMMAND)) ; allow the mouse to work!
(UNLESS (CHARACTERP CHAR) ; eliminate mouse clicks now
(SETQ INPUT-DONE T)
;; This is admittedly a kludge, but it's the simplest way to
;; get EDITOR-INPUT to execute the mouse-clicked command
(SETQ *YANKED-MINI-BUFFER-COMMAND* CHAR)
(GO CHECK-FOR-INPUT))
(SETQ XCHAR (CHAR-UPCASE CHAR))
(COND ((NOT (OR (NOT (ZEROP (CHAR-BITS CHAR)))
(CHAR-EQUAL CHAR #\ALTMODE) (CHAR-EQUAL CHAR #\END)
(CHAR-EQUAL CHAR #\RUBOUT) (CHAR-EQUAL CHAR #\CLEAR-INPUT)
(CHAR-EQUAL CHAR #\HELP) (CHAR-EQUAL CHAR #\SCROLL)
(MEM #'CHAR-EQUAL CHAR TV:KBD-INTERCEPTED-CHARACTERS)))
(GO NORMAL))
((MEMQ XCHAR '(#\c-S #\c-R))
(PUSH-ISEARCH-STATUS)
(ASET ':REPEAT *IS-OPERATION* P)
(LET ((NEW-REVERSE-P (CHAR= XCHAR #\c-R)))
(COND ;; In reverse mode, just go to forward.
((NEQ (AREF *IS-REVERSE-P* P) NEW-REVERSE-P)
(ASET NEW-REVERSE-P *IS-REVERSE-P* P)
(SETQ MUST-REDIS T)
(ASET ':REVERSE *IS-OPERATION* P))
((ZEROP (AREF *IS-POINTER* P))
(LET ((STRING (STRING (OR (CAAR *SEARCH-RING*) (BARF)))))
(IF *RUBOUT-KILLS-LAST-SEARCH-STRING*
(PROGN
(COPY-ARRAY-CONTENTS STRING *IS-STRING*)
(ASET (ARRAY-ACTIVE-LENGTH STRING) *IS-POINTER* P))
(LOOP FOR MORE FIRST NIL THEN T
FOR CHAR BEING THE ARRAY-ELEMENTS OF STRING
WHEN MORE
DO (PUSH-ISEARCH-STATUS)
DO (LET ((IDX (AREF *IS-POINTER* P)))
(AND ( IDX (ARRAY-LENGTH *IS-STRING*))
(ADJUST-ARRAY-SIZE *IS-STRING* (+ IDX 100)))
(ASET CHAR *IS-STRING* IDX)
(ASET (1+ IDX) *IS-POINTER* P))
(ASET ':NORMAL *IS-OPERATION* P))))
(SETQ MUST-REDIS T))))
(GO CHECK-FOR-INPUT))
((CHAR= XCHAR #\c-Q)
(SETQ CHAR (MAKE-CHAR (EDITOR-INPUT)))
(GO NORMAL))
((CHAR= XCHAR #\c-G)
(COND ((AND (OR SUPPRESSED-REDISPLAY (NEQ (AREF *IS-STATUS* P) T))
(PLUSP P))
;; G in other than a successful search
;; rubs out until it becomes successful.
(SETQ P (DO ((P (1- P) (1- P)))
((EQ (AREF *IS-STATUS* P) T) P)))
(SETQ P1 (MIN P P1) MUST-REDIS T)
(GO CHECK-FOR-INPUT))
(T
(MOVE-POINT (AREF *IS-BP* 0))
(FUNCALL *TYPEIN-WINDOW* ':MAKE-COMPLETE)
(RETURN NIL))))
((MEMQ CHAR TV:KBD-INTERCEPTED-CHARACTERS)
(ZWEI-KBD-INTERCEPT-CHARACTER CHAR *TYPEIN-WINDOW*)
(GO CHECK-FOR-INPUT))
((OR (CHAR= CHAR #\ALTMODE) (CHAR= CHAR #\END))
(AND (ZEROP P)
(RETURN (LET ((*CURRENT-COMMAND* 'COM-STRING-SEARCH))
(COM-STRING-SEARCH-INTERNAL REVERSE-P NIL NIL NIL))))
(SETQ INPUT-DONE T)
(GO CHECK-FOR-INPUT))
((CHAR= CHAR #\RUBOUT)
(COND (( P 0) ; If he over-rubbed out,
(BEEP) ; that is an error.
(GO CHECK-FOR-INPUT))
(T
;; Rubout pops all of these PDLs.
(SETQ P (1- P))
(SETQ P1 (MIN P P1))
(SETQ MUST-REDIS T)
(GO CHECK-FOR-INPUT))))
((CHAR= CHAR #\CLEAR-INPUT)
(SETQ P 0 P1 0 MUST-REDIS T)
(GO CHECK-FOR-INPUT))
((CHAR= CHAR #\HELP)
(PRINT-DOC ':FULL *CURRENT-COMMAND*)
(FORMAT T "~2&Type any character to flush:")
(CHECK-FOR-TYPEOUT-WINDOW-TYPEOUT)
(GO CHECK-FOR-INPUT))
(T
(FUNCALL STANDARD-INPUT ':UNTYI REAL-CHAR)
(SETQ INPUT-DONE T)
(GO CHECK-FOR-INPUT)))
(FERROR NIL "A clause fell through.")

;; Normal chars to be searched for come here.
NORMAL
(OR MUST-REDIS (TYPEIN-LINE-MORE "~C" CHAR))
(PUSH-ISEARCH-STATUS)
(LET ((IDX (AREF *IS-POINTER* P)))
(AND ( IDX (ARRAY-LENGTH *IS-STRING*))
(ADJUST-ARRAY-SIZE *IS-STRING* (+ IDX 100)))
(WHEN (CHAR-FAT-P CHAR)
(UNLESS (STRING-FAT-P *IS-STRING*)
(LET ((NEW-STRING (MAKE-ARRAY (ARRAY-LENGTH *IS-STRING*)
:FILL-POINTER (FILL-POINTER *IS-STRING*)
:TYPE 'ART-FAT-STRING)))
(COPY-ARRAY-CONTENTS *IS-STRING* NEW-STRING)
(STRUCTURE-FORWARD *IS-STRING* NEW-STRING 2 2)
(SETQ *IS-STRING* NEW-STRING))))
(ASET CHAR *IS-STRING* IDX)
(ASET (1+ IDX) *IS-POINTER* P))
(ASET ':NORMAL *IS-OPERATION* P)
;; Come here after possibly processing input to update the search tables
;; to search for a while. First, if necessary and not suppressed
;; update the search string displayed in the echo area.
CHECK-FOR-INPUT
;; If there is input available, go read it.
;; Otherwise, do work if there is work to be done.
(AND (NOT INPUT-DONE)
(FUNCALL STANDARD-INPUT ':LISTEN)
(GO INPUT))
;; Now do some work for a while, then go back to CHECK-FOR-INPUT.
(COND (MUST-REDIS
(SETQ MUST-REDIS NIL)
(TYPEIN-LINE "~:|")
(OR (AREF *IS-STATUS* P1) (TYPEIN-LINE-MORE "Failing "))
(AND (AREF *IS-REVERSE-P* P) (TYPEIN-LINE-MORE "Reverse "))
(TYPEIN-LINE-MORE "I-Search: ")
(STORE-ARRAY-LEADER (AREF *IS-POINTER* P) *IS-STRING* 0)
(TYPEIN-LINE-MORE "~A" *IS-STRING*)))
;; Now see what sort of state the actual search is in, and
;; what work there is to do. P1 points at the level of the
;; table on which we are actually working.
(MOVE-BP BP1 (AREF *IS-BP* P1))
;; Display point at the end of the last search level which has succeeded.
(DO ((P0 P1 (1- P0)))
((EQ (AREF *IS-STATUS* P0) T)
(MOVE-POINT (AREF *IS-BP* P0))))
(MUST-REDISPLAY *WINDOW* DIS-BPS)
(COND ((EQ (AREF *IS-STATUS* P1) ':GO)

;; If we are about to repeat a search, generate the Boyer-Moore
;; tables for the pattern string and cache them. Do not generate
;; the tables if they are already cached.
(IF (OR TIME-OUT (CHAR= XCHAR #\c-S))
(WHEN (AND (NULL OLD-SKIP-TABLE) (NULL OLD-REOCCURRENCE-TABLE))
(SETQ OLD-SKIP-TABLE (GENERATE-BOYER-SKIP-TABLE
*IS-STRING* SKIP-RESOURCE)
OLD-REOCCURRENCE-TABLE (GENERATE-BOYER-REOCCURRENCE-TABLE
*IS-STRING*)))
(SETQ OLD-SKIP-TABLE NIL
OLD-REOCCURRENCE-TABLE NIL))
;; We need an additional check here, because of the interaction between
;; additional c-S'es and typeahead. If you type, say "FEPFS" c-S in a
;; long buffer with "FEP" at the beginning of the buffer and "FEPFS" at
;; the end of the buffer, then *IS-STRING* can get out of sync with the
;; reoccurrence table. This code gets them back in sync.
(WHEN (AND OLD-REOCCURRENCE-TABLE
( (ARRAY-LENGTH OLD-REOCCURRENCE-TABLE)
(STRING-LENGTH *IS-STRING*)))
(SETQ OLD-SKIP-TABLE (GENERATE-BOYER-SKIP-TABLE
*IS-STRING* SKIP-RESOURCE)
OLD-REOCCURRENCE-TABLE (GENERATE-BOYER-REOCCURRENCE-TABLE
*IS-STRING*)))
(SETQ SKIP-TABLE OLD-SKIP-TABLE
REOCCURRENCE-TABLE OLD-REOCCURRENCE-TABLE)

;; If the level we were working on is still not finished,
;; search at most 100 more lines. If we find it or the end of the buffer
;; before then, this level is determined and we can work on the next.
;; Otherwise, we remain in the :GO state and do 100 more lines next time.
(MULTIPLE-VALUE (NEW-BP TIME-OUT)
(SEARCH BP1 *IS-STRING*
(AREF *IS-REVERSE-P* P1) NIL 100
NIL *ALPHABETIC-CASE-AFFECTS-SEARCH* ;---
SKIP-TABLE REOCCURRENCE-TABLE))
;; What happened?
(COND (TIME-OUT
;; Nothing determined. NEW-BP has where we stopped.
(MOVE-BP BP1 NEW-BP))
((NULL NEW-BP)
;; This search was determined to be a failure.
(OR (AND (MEMQ ':MACRO-ERROR
(FUNCALL STANDARD-INPUT ':WHICH-OPERATIONS))
(FUNCALL STANDARD-INPUT ':MACRO-ERROR))
(BEEP))
(ASET NIL *IS-STATUS* P1)
(MOVE-BP BP1 (AREF *IS-BP* (1- P1)))
(MOVE-POINT BP1)
(SETQ MUST-REDIS T))
(T ;; This search level has succeeded.
(ASET T *IS-STATUS* P1)
(MOVE-POINT NEW-BP)
(MOVE-BP BP1 NEW-BP))))
(( P P1)
;; This level is finished, but there are more pending levels typed ahead.
(SETQ P1 (1+ P1))
(ASET (SETQ BP1 (COPY-BP BP1)) *IS-BP* P1)
(STORE-ARRAY-LEADER (AREF *IS-POINTER* P1) *IS-STRING* 0)
(COND ((NULL (AREF *IS-STATUS* (1- P1)))
(COND ((NEQ (AREF *IS-OPERATION* P1) ':REVERSE)
;; A failing search remains so unless we reverse direction.
(ASET NIL *IS-STATUS* P1))
(T ;; If we reverse direction, change prompt line.
(SETQ MUST-REDIS T))))
((EQ (AREF *IS-OPERATION* P1) ':NORMAL)
;; Normal char to be searched for comes next.
;; We must adjust the bp at which we start to search
;; so as to allow the user to extend the string already found.
(MOVE-BP
BP1 (FORWARD-CHAR
BP1 (COND ((AREF *IS-REVERSE-P* P1)
(COND ((= (ARRAY-ACTIVE-LENGTH *IS-STRING*) 1)
0)
(T (ARRAY-ACTIVE-LENGTH *IS-STRING*))))
(T (- 1 (ARRAY-ACTIVE-LENGTH *IS-STRING*))))
T)))))
;; If there is nothing left to do, and terminator seen, exit.
(INPUT-DONE
(SEARCH-RING-PUSH
;; Entries on the search ring should have a leader
(STRING-NCONC (MAKE-ARRAY (ARRAY-ACTIVE-LENGTH *IS-STRING*)
':TYPE (ARRAY-TYPE *IS-STRING*)
':FILL-POINTER 0)
*IS-STRING*)
'SEARCH)
(TYPEIN-LINE-MORE "~C" #\END)
(MAYBE-PUSH-POINT ORIG-PT)
(SELECT-WINDOW *WINDOW*)
(RETURN NIL))
;; Nothing to do and no terminator, wait for input.
(T (GO INPUT)))
(GO CHECK-FOR-INPUT)

)))
(FUNCALL *MODE-LINE-WINDOW* ':DONE-WITH-MODE-LINE-WINDOW))
DIS-BPS)

How about this? This is clearly from aliens.


(DEFMFUN MEVAL1 (FORM)
(declare (special nounl *break-points* *break-step*))
(COND ((ATOM FORM)
(PROG (VAL)
(COND ((NOT (SYMBOLP FORM)) (RETURN FORM))
((AND $NUMER (SETQ VAL (SAFE-MGET FORM '$NUMER))
(OR (NOT (EQ FORM '$%E)) $%ENUMER))
(RETURN (MEVAL1 VAL)))
((NOT (BOUNDP FORM))
(IF (SAFE-GET FORM 'BINDTEST)
(MERROR "~:M unbound variable" FORM)
(RETURN FORM)))
((MFILEP (SETQ VAL (SYMBOL-VALUE FORM)))
(SETQ VAL
(EVAL (DSKGET (CADR VAL) (CADDR VAL) 'VALUE NIL)))))
(WHEN (AND $REFCHECK (MEMQ FORM (CDR $VALUES))
(NOT (MEMQ FORM REFCHKL)))
(SETQ REFCHKL (CONS FORM REFCHKL))
(MTELL "~:M has value.~%" FORM))
(RETURN VAL)))
((OR (AND (ATOM (CAR FORM))
(SETQ FORM (CONS (NCONS (CAR FORM)) (CDR FORM))))
(ATOM (CAAR FORM)))
(LET ((BAKTRCL BAKTRCL) TRANSP)
(PROG (U ARYP)
(declare (special aryp))
;;(COND ((EQ DEBUG '$ALL) (SETQ BAKTRCL (CONS FORM BAKTRCL))))
(setq *last-meval1-form* form)
(SETQ ARYP (MEMQ 'array (CDAR FORM)))
(COND ((AND (NOT OPEXPRP) (NOT ARYP)
(MEMQ (CAAR FORM) '(MPLUS MTIMES MEXPT MNCTIMES)))
(GO C))
;; dont bother pushing mplus and friends on baktrcl
;; should maybe even go below aryp.
((AND debug
(PROGN
;(SETQ BAKTRCL (CONS FORM BAKTRCL))
;; if wanting to step, the *break-points*
;; variable will be set to a vector (possibly empty).
(when (and *break-points*
(or (null *break-step*)
(null (funcall *break-step* form))))
(let ((ar *break-points*))
(declare (type (vector t) ar))
(sloop for i below (fill-pointer ar)
when (eq (car (aref ar i)) form)
do (*break-points* form)
(loop-finish))))
NIL)))
((AND $SUBSCRMAP ARYP
(DO ((X (MARGS FORM) (CDR X)))
((OR (NULL X) (MXORLISTP (CAR X))) X)))
(SETQ NOEVALARGS NIL) (RETURN (SUBGEN FORM)))
((EQ (CAAR FORM) 'MQAPPLY) (RETURN (MQAPPLY1 FORM))))
(BADFUNCHK (CAAR FORM) (CAAR FORM) NIL)
A (SETQ U (OR (SAFE-GETL (CAAR FORM) '(NOUN))
(AND NOUNSFLAG (EQ (GETCHAR (CAAR FORM) 1) '%)
(NOT (OR (GETL-FUN (CAAR FORM)
'(SUBR FSUBR LSUBR))
(SAFE-GETL (CAAR FORM)
'(MFEXPR* MFEXPR*S))))
(PROG2 ($VERBIFY (CAAR FORM))
(SAFE-GETL (CAAR FORM) '(NOUN))))
(AND (NOT ARYP) $TRANSRUN
(SETQ TRANSP
(OR (SAFE-MGETL (CAAR FORM) '(T-MFEXPR))
(SAFE-GETL (CAAR FORM)
'(TRANSLATED-MMACRO)))))
(AND (NOT ARYP)
(SETQ U
(OR (SAFE-MGET (CAAR FORM) 'TRACE)
(AND $TRANSRUN
(SAFE-GET (CAAR FORM) 'TRANSLATED)
(NOT (SAFE-MGET (CAAR FORM)
'LOCAL-FUN))
(SETQ TRANSP T) (CAAR FORM))))
(GETL-FUN U '(EXPR SUBR LSUBR)))
(COND (ARYP (SAFE-MGETL (CAAR FORM) '(HASHAR ARRAY)))
((SAFE-MGETL (CAAR FORM) '(MEXPR MMACRO)))
((SAFE-MGETL (CAAR FORM) '(T-MFEXPR)))
(T (OR (SAFE-GETL (CAAR FORM)
'(MFEXPR* MFEXPR*S))
(GETL-FUN (CAAR FORM)
'(SUBR FSUBR EXPR FEXPR macro
LSUBR)))))))
(COND ((NULL U) (GO B))
((AND (MEMQ (CAR U) '(MEXPR MMACRO)) (MFILEP (CADR U)))
(SETQ U (LIST (CAR U)
(DSKGET (CADADR U) (CAR (CDDADR U))
(CAR U) NIL))))
((AND (MEMQ (CAR U) '(ARRAY HASHAR)) (MFILEP (CADR U)))
(I-$UNSTORE (NCONS (CAAR FORM)))
(RETURN (MEVAL1 FORM))))
(RETURN
(COND ((EQ (CAR U) 'HASHAR)
(HARRFIND (CONS (CAR FORM) (MEVALARGS (CDR FORM)))))
((MEMQ (CAR U) '(FEXPR FSUBR))
(IF FEXPRERRP
(MERROR "Attempt to call ~A ~A from MACSYMA level.~
~%Send a bug note."
(CAR U) (CAAR FORM)))
(SETQ NOEVALARGS NIL) (APPLY (CAAR FORM) (CDR FORM)))
((OR (AND (EQ (CAR U) 'SUBR)
(PROG2 (MARGCHK (CAAR FORM) (CDR FORM)) T))
(EQ (CAR U) 'LSUBR))
; ((MEMQ (CAR U) '(SUBR LSUBR))
; (MARGCHK (CAAR FORM) (CDR FORM)))
(APPLY (CAAR FORM) (MEVALARGS (CDR FORM))))

((EQ (CAR U) 'NOUN)
; (MARGCHK (CAAR FORM) (CDR FORM))
(COND ((OR (MEMQ (CAAR FORM) NOUNL) NOUNSFLAG)
(SETQ FORM (CONS (CONS (CADR U) (CDAR FORM))
(CDR FORM)))
(GO A))
(ARYP (GO B))
((MEMQ (CAAR FORM) '(%SUM %PRODUCT))
(SETQ U (DO%SUM (CDR FORM) (CAAR FORM))
NOEVALARGS NIL)
(CONS (NCONS (CAAR FORM)) U))
(T (MEVAL2 (MEVALARGS (CDR FORM)) FORM))))
((EQ (CAR U) 'array)
(ARRFIND (CONS (CAR FORM) (MEVALARGS (CDR FORM)))))
((EQ (CAR U) 'MEXPR)
(MLAMBDA (CADR U) (CDR FORM) (CAAR FORM) NOEVALARGS form))
((MEMQ (CAR U) '(MMACRO TRANSLATED-MMACRO))
(SETQ NOEVALARGS NIL)
(MEVAL (MMACRO-APPLY (CADR U) FORM)))
((EQ (CAR U) 'MFEXPR*)
(SETQ NOEVALARGS NIL) (APPLY (CADR U) (NCONS FORM)))
#+cl
((eq (car u) 'macro)
(setq noevalargs nil)
(setq form (cons(caar form) (cdr form)))
; (setf (car form) (caar form) )
(eval form)
)
#+Maclisp
((EQ (CAR U) 'MFEXPR*S)
(SETQ NOEVALARGS NIL)
;; use macsyma Trace if you want to trace this call.
(SUBRCALL T (CADR U) FORM))
((EQ (CAR U) 'T-MFEXPR) (APPLY (CADR U) (CDR FORM)))
(T (MARGCHK (CAAR FORM) (CDR FORM))
(APPLY (CADR U) (MEVALARGS (CDR FORM))))))
B #+(OR PDP10 Multics Franz NIL cl)
(IF (AND (NOT ARYP) (LOAD-FUNCTION (CAAR FORM) T)) (GO A))
(BADFUNCHK (CAAR FORM) (CAAR FORM) NIL)
(IF (SYMBOLP (CAAR FORM))
(SETQ U (BOUNDP (CAAR FORM)))
(RETURN (MEVAL1-EXTEND FORM)))
C (COND ((OR (NULL U)
(AND (SAFE-GET (CAAR FORM) 'OPERATORS) (NOT ARYP))
(EQ (CAAR FORM) (SETQ U (SYMBOL-VALUE (CAAR FORM)))))
(SETQ FORM (MEVAL2 (MEVALARGS (CDR FORM)) FORM))
(RETURN (OR (AND (SAFE-MGET (CAAR FORM) 'ATVALUES)
(AT1 FORM)) FORM)))
((AND ARYP (SAFE-GET (CAAR FORM) 'NONARRAY))
(RETURN (CONS (CONS (CAAR FORM) ARYP)
(MEVALARGS (CDR FORM)))))
((ATOM U)
(BADFUNCHK (CAAR FORM) U NIL)
(SETQ FORM (CONS (CONS (GETOPR U) ARYP) (CDR FORM)))
(GO A))
((EQ (CAAR U) 'LAMBDA)
(IF ARYP
(MERROR "Improper array call")
(RETURN (MLAMBDA U (CDR FORM)
(CAAR FORM) NOEVALARGS form))))
(T (RETURN (MAPPLY1 U (MEVALARGS (CDR FORM))
(CAAR FORM) form)))))))
(T (MAPPLY1 (CAAR FORM) (MEVALARGS (CDR FORM)) (CAAR FORM) form))))

André Thieme

unread,
Feb 28, 2007, 10:03:58 AM2/28/07
to
job-27...@craigslist.org schrieb:

> Another friend of mine commenting on the same FizzBuzz thread supplied
> the following Python code. It certainly is concise:
>
> for i in xrange(1,101):
> print(str(i), "Fizz", "Buzz", "FizzBuzz")[(i%3==0)|(i%5==0)<<1]
>

> Forgive my ignorance, but is anything like the boolean bit shifting
> technique used in the Python code above possible in Lisp? No big loss if
> it isn't, just curious.

In principle you could do exactly that - if you also provide this
strange shifting operator:

>>> (11%3==0)|(11%5==0)
False
>>> (12%3==0)|(12%5==0)
True
>>> (20%3==0)|(20%5==0)
True
>>> (30%3==0)|(30%5==0)
True


And:

>>> False<<1
0
>>> True<<1
2

Okay, so we expect << to throw out 0, 2, 2 and 2 for the three cases.
But:

>>> (11%3==0)|(11%5==0)<<1
0
>>> (12%3==0)|(12%5==0)<<1
1
>>> (20%3==0)|(20%5==0)<<1
2
>>> (30%3==0)|(30%5==0)<<1
3

It seems truth values would be structs in Lisp. They not only have a
boolean value of t or nil, but they also represent some number.

Anyway, let's say you provide <<, then you could say:

(loop for i upto 100 do
(print (nth (<< (mod i 3) (mod i 5)) (list i "Fizz" "Buzz" "FizzBuzz")))

This version has now a smaller complexity with its 20 tokens as the
Python version with its 28 tokens.
Okay, if you count the definition of << in Lisp, then Python wins here,
because it already is defined there.


> I suppose it's unreasonable to expect the Lisp version to be as concise
> as the Python version

No, it is reasonable. And as you see: Python is complexity wise 40% worse.


> - not only is this a toy example, but I think a
> language with more syntax will be able to provide more brevity in
> certain situations.

You are right. For people who are happy to write scripts Python will
be an excellent choice. Scripting is the domain of Python and usually
it will do very well with 10 line programs.
But if we are talking about something that goes beyond 3k LOC things
change. Then Lisp will have a more specialized syntax and win.
So it is the question of complexity. For easy problems Lisp will most
likely not be much better than Python - but probably also not worse.
But as complexity grows you are better off with Lisp.

> That's a tradeoff I'm willing to accept given the
> benefits of a syntax that's more readily parsed and manipulated.

Clever choice.


André
--

Richard M Kreuter

unread,
Feb 28, 2007, 10:40:10 AM2/28/07
to

"Tim Bradshaw" <tfb+g...@tfeb.org> writes:

> Incidentally, I'm deeply disappointed in the quality of answers in
> this thread. In the elder days there would have been at least a few
> followups showing how to do this in the proper "FORMAT string
> indistinguishable from line noise" way. No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.

(dotimes (i 100)
(format t "~[~[FizzBuzz~:;Fizz~]~:;~[Buzz~:;~D~]~]~%" (mod i 3) (mod i 5) i))

Best I could do on one cup of coffee.

--
RmK

justinhj

unread,
Feb 28, 2007, 10:51:05 AM2/28/07
to

Unless I'm misunderstanding something what doesn't work? The output is
correct.

Justin


Barry Fishman

unread,
Feb 28, 2007, 11:05:19 AM2/28/07
to
"Tim Bradshaw" <tfb+g...@tfeb.org> writes:
> Incidentally, I'm deeply disappointed in the quality of answers in
> this thread. In the elder days there would have been at least a few
> followups showing how to do this in the proper "FORMAT string
> indistinguishable from line noise" way. No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.

I'm not a good practitioner of FORMAT, but there needs to be at least
one FORMAT solution presented.

(defun fizz-buzz (n)
(loop for i from 1 to n do
(format t "~[~d~;Fizz~;Buzz~;FizzBuzz~]~%"


(+ (if (zerop (mod i 3)) 1 0)
(if (zerop (mod i 5)) 2 0))

i)))

--
Barry Fishman

justinhj

unread,
Feb 28, 2007, 11:31:57 AM2/28/07
to

I'm not having much luck trying to make it elegant and concise but I
quite like this recursive version...

(defun fizzbuzz2(n)
(labels ((fizzbuzz-rec (current last fizz buzz)
(let ((output nil))
(if (= 0 fizz)
(push "fizz" output))
(if (= 0 buzz)
(push "buzz" output))
(if output
(format t "~{~a~}~%" output))
(unless (= current last)
(fizzbuzz-rec (1+ current) last (mod (1-
fizz) 3) (mod (1- buzz) 5))))))
(fizzbuzz-rec 1 n (1- 3) (1- 5))))

CL-USER> (fizzbuzz2 15)


fizz
buzz
fizz
fizz
buzz
fizz

buzzfizz


Justin

Raffael Cavallaro

unread,
Feb 28, 2007, 11:37:01 AM2/28/07
to
On 2007-02-28 02:33:08 -0500, job-27...@craigslist.org said:

> Looks like read macros are involved, but I don't want to get ahead of myself...

To clarify, rather than use mod, Rob's version has loop iterate on two
circular lists, one representing (mod i 3) the other representing (mod
i 5). The #integer= and #integer# notations allow the lisp reader to
construct wholly or partially self-referential list structure. Heed
Rob's warning about setting *print-circle* to t unless you want a stack
overflow.

Tim Bradshaw

unread,
Feb 28, 2007, 11:53:42 AM2/28/07
to
On Feb 28, 3:40 pm, Richard M Kreuter <kreu...@progn.net> wrote:

> (dotimes (i 100)
> (format t "~[~[FizzBuzz~:;Fizz~]~:;~[Buzz~:;~D~]~]~%" (mod i 3) (mod i 5) i))
>
> Best I could do on one cup of coffee.

Good, but you'd get extra points for avoiding the explicit FizzBuzz in
there. Off the top of my head I think:
(format t "~&~[Fizz~;~]~[Buzz~;~D~]~%" (mod i 3) (mod i 5) i). But
I'd have to check, and it's not really squiggly enough.

--tim

nall...@gmail.com

unread,
Feb 28, 2007, 12:32:19 PM2/28/07
to

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

CL-USER> (macroexpand-1 '(def-fizz-buzz))

=>

(DEFUN FIZZ-BUZZ (X)
(LET ((Y 0))
(LOOP (INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Buzz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Fizz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Buzz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Buzz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Fizz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "Buzz")
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT Y)
(INCF Y)
(WHEN (> Y X) (RETURN))
(PRINT "FizzBuzz"))))

Richard M Kreuter

unread,
Feb 28, 2007, 12:38:48 PM2/28/07
to
"Tim Bradshaw" <tfb+g...@tfeb.org> writes:

I think you mean "~&~[Fizz~:;~]~[Buzz~:;~D~]~%" right?

Something like that was my first hunch, but it will print the integer
next to Fizz when (and (zerop (mod i 3)) (plusp (mod i 5))),

* (let ((i 6)) (format t "~&~[Fizz~:;~]~[Buzz~:;~D~]~%" (mod i 3) (mod i 5) i))
-| Fizz6
=> NIL
* (let ((i 9)) (format t "~&~[Fizz~:;~]~[Buzz~:;~D~]~%" (mod i 3) (mod i 5) i))
-| Fizz9
=> NIL

Here's one that avoids the explicit FizzBuzz, and adds /seven/
squiggles to my first stab:

(dotimes (i 100)
(format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"
(mod i 3) (mod i 5) i "Fizz" "Buzz"))

Better?

--
RmK

John Thingstad

unread,
Feb 28, 2007, 1:09:42 PM2/28/07
to
On Wed, 28 Feb 2007 18:38:48 +0100, Richard M Kreuter <kre...@progn.net>
wrote:

>
> (dotimes (i 100)


> (format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"
> (mod i 3) (mod i 5) i "Fizz" "Buzz"))
>
> Better?
>

Perfect! I rivals Perl in clarity of exposition..

Frank Buss

unread,
Feb 28, 2007, 1:39:19 PM2/28/07
to
job-27...@craigslist.org wrote:

> 1) In "ANSI Common Lisp", Graham makes the following comments:
> "The loop macro was originally designed to help inexperienced Lisp
> users write iterative code...Unfortunately, loop is more like English
> than its designers ever intended...to understand it in the abstract is
> almost impossible...For such reasons, the use of loop cannot be
> recommended."

I don't agree. You can learn the basics by reading loop examples. Once you
know how each loop word works, all you need is the BNF, if you forgot
something. The more you use it, the more you learn the grammar and the
easier it is to use it. Like all good DSLs, it helps a lot to write short
and easy to understand code for the task for which it is designed:
iterating.

--
Frank Buss, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de

Frank Buss

unread,
Feb 28, 2007, 1:47:04 PM2/28/07
to
Ken Tilton wrote:

> You refer of course to the Cello code to create a 3-D oblong button of
> variable thickness with rounded corners of variable radius:

Yes, that's really ugly :-) Did you thought about using your dataflow
paradigm to create graphics? Something similar would be to use higher order
functions and combinators. The code below creates buttons like this:

http://www.frank-buss.de/tmp/buttons.png

In LispWorks 4.3.7 it is not very fast, but SBCL needs not many seconds :-)

(defparameter *width* 200)
(defparameter *height* 70)
(defstruct color
(r 0.0 :type single-float)
(g 0.0 :type single-float)
(b 0.0 :type single-float)
(a 0.0 :type single-float))

(defun rgb-2-color (rgb)
(declare (type fixnum rgb)
(make-color :r (/ (ldb (byte 8 16) rgb) 255.0)
:g (/ (ldb (byte 8 8) rgb) 255.0)
:b (/ (ldb (byte 8 0) rgb) 255.0)))

(defun middle (a b)
(/ (+ a b) 2.0))

(defconstant +transparent+ (make-color :r 1.0 :g 1.0 :b 1.0 :a 1.0))
(defconstant +white+ (make-color :r 1.0 :g 1.0 :b 1.0))

;(defconstant +color0+ (rgb-2-color #xeaeaa2))
;(defconstant +color1+ (rgb-2-color #xee4f23))

;(defconstant +color0+ (rgb-2-color #x999999))
;(defconstant +color1+ (rgb-2-color #x444444))

(defconstant +color0+ (rgb-2-color #xa2fafa))
(defconstant +color1+ (rgb-2-color #x23ee4f))

(defconstant +color-avg+
(make-color :r (middle (color-r +color0+) (color-r +color1+))
:g (middle (color-g +color0+) (color-g +color1+))
:b (middle (color-b +color0+) (color-b +color1+))))

(defun transparentp (color)
(= 1.0 (color-a color)))

(defun disc (&key x0 y0 radius)
(let ((r2 (* radius radius)))
(lambda (x y)
(let ((xc (- x x0))
(yc (- y y0)))
(let ((r (+ (* xc xc) (* yc yc))))
(<= r r2))))))

(defun rect (&key x0 y0 x1 y1)
(lambda (x y)
(and (>= x x0)
(<= x x1)
(>= y y0)
(<= y y1))))

(defun rounded-rect (&key x0 y0 x1 y1 radius)
(let ((x0-i (+ x0 radius))
(y0-i (+ y0 radius))
(x1-i (- x1 radius))
(y1-i (- y1 radius)))
(lambda (x y)
(let ((rect (rect :x0 x0 :y0 y0 :x1 x1 :y1 y1))
(top-left-disc (disc :x0 x0-i :y0 y0-i :radius radius))
(top-right-disc (disc :x0 x1-i :y0 y0-i :radius radius))
(bottom-left-disc (disc :x0 x0-i :y0 y1-i :radius radius))
(bottom-right-disc (disc :x0 x1-i :y0 y1-i :radius radius)))
(cond ((and (< x x0-i) (< y y0-i)) (funcall top-left-disc x y))
((and (> x x1-i) (< y y0-i)) (funcall top-right-disc x y))
((and (< x x0-i) (> y y1-i)) (funcall bottom-left-disc x y))
((and (> x x1-i) (> y y1-i)) (funcall bottom-right-disc x y))
(t (funcall rect x y)))))))

(defun gradient (&key color0 color1 x0 x1)
(let ((r0 (color-r color0))
(g0 (color-g color0))
(b0 (color-b color0))
(r1 (color-r color1))
(g1 (color-g color1))
(b1 (color-b color1))
(dx (- x1 x0)))
(lambda (x)
(cond ((< x x0) color0)
((>= x x1) color1)
(t (setf x (/ (- x x0) dx))
(make-color :r (+ (* (- r1 r0) x) r0)
:g (+ (* (- g1 g0) x) g0)
:b (+ (* (- b1 b0) x) b0)))))))

(defun blur (&key mask)
(lambda (x y)
(let ((sum 0.0))
(loop for xo from -4 to 4 do
(loop for yo from -4 to 4 do
(when (funcall mask (+ x xo) (+ y yo)) (incf sum (/ (+ (*
xo xo) (* yo yo) 2.0))))))
(/ sum 2.0))))

(defun add (fun1 fun2)
(lambda (x y)
(let ((c1 (funcall fun1 x y))
(c2 (funcall fun2 x y)))
(cond ((transparentp c1) c2)
((transparentp c2) c1)
(t (make-color :r (+ (color-r c1) (color-r c2))
:g (+ (color-g c1) (color-g c2))
:b (+ (color-b c1) (color-b c2))))))))

(defun neg-mul (color-fun channel-fun)
(lambda (x y)
(let ((color (funcall color-fun x y))
(channel (funcall channel-fun x y)))
(cond ((transparentp color) color)
(t (when (> channel 1) (setf channel 1))
(when (< channel 0) (setf channel 0))
(let ((channel2 (- 1 channel)))
(make-color :r (+ (* channel (color-r +color0+)) (*
channel2 (color-r color)))
:g (+ (* channel (color-g +color0+)) (* channel2 (color-g color)))
:b (+ (* channel (color-b +color0+)) (* channel2 (color-b color))))))))))

(defun fill-gradient-vertical (&key gradient function)
(lambda (x y)
(if (funcall function x y)
(funcall gradient y)
+transparent+)))

(defun fill-solid (&key function color)
(lambda (x y)
(if (funcall function x y)
color
+transparent+)))

(defun overlay (&key background foreground)
(lambda (x y)
(let ((foreground-color (funcall foreground x y)))
(if (transparentp foreground-color)
(funcall background x Y)
foreground-color))))

(defun xor (fun1 fun2)
(lambda (x y)
(not (eql (funcall fun1 x y) (funcall fun2 x y)))))

(defun channel-and (channel-fun binary-fun)
(lambda (x y)
(if (funcall binary-fun x y)
(funcall channel-fun x y)
0)))

(defun button (&key x0 y0 x1 y1)
(let* ((radius 15.0)
(stroke 1.0)
(outer-rect (rounded-rect :x0 (- x0 stroke) :y0 (- y0 stroke)
:x1 (+ x1 stroke) :y1 (+ y1 stroke)
:radius (+ radius stroke)))
(inner-rect (rounded-rect :x0 x0 :y0 y0
:x1 x1 :y1 y1
:radius radius))
(ring (xor outer-rect inner-rect))
(inner-glow (channel-and (blur :mask ring) inner-rect))
(gradient (gradient :color0 +color0+ :color1 +color1+ :x0 y0 :x1
y1))
(filled-outer-rect (fill-solid
:function outer-rect
:color +color-avg+))
(filled-inner-rect (fill-gradient-vertical
:gradient gradient
:function inner-rect)))
(overlay :background filled-outer-rect
:foreground (neg-mul filled-inner-rect inner-glow))))

(defun anti-alias (function)
(lambda (x y)
(let ((c0 (funcall function x y))
(c1 (funcall function (+ x 0.5) y))
(c2 (funcall function x (+ y 0.5)))
(c3 (funcall function (+ x 0.5) (+ y 0.5))))
(make-color :r (/ (+ (color-r c0) (color-r c1) (color-r c2) (color-r
c3)) 4.0)
:g (/ (+ (color-g c0) (color-g c1) (color-g c2) (color-g c3)) 4.0)
:b (/ (+ (color-b c0) (color-b c1) (color-b c2) (color-b c3)) 4.0)
:a (/ (+ (color-a c0) (color-a c1) (color-a c2) (color-a c3)) 4.0)))))

(defun color-byte (color)
(let ((result (floor (* 255.0 color))))
(cond ((> result 255) 255)
((< result 0) 0)
(t result))))

(defun red-byte (color)
(color-byte (color-r color)))

(defun green-byte (color)
(color-byte (color-g color)))

(defun blue-byte (color)
(color-byte (color-b color)))

(defun paint (function &optional (filename "c:/tmp/test.tga"))
(with-open-file
(tga filename
:direction :output
:if-exists :supersede
:element-type 'unsigned-byte)
(dolist (byte (list 0 0 2 0 0 0 0 0 0 0 0 0
(mod *width* 256) (floor *width* 256)
(mod *height* 256) (floor *height* 256) 24 0))
(write-byte byte tga))
(loop for y from (1- *height*) downto 0 do
(loop for x from 0 below *width* do
(let ((color (funcall function x y)))
(when (transparentp color) (setf color +white+))
(write-byte (blue-byte color) tga)
(write-byte (green-byte color) tga)
(write-byte (red-byte color) tga)))))
#+:lispworks (sys:call-system (format nil
"c:\\Programme\\Adobe\\Photoshop 7.0\\Photoshop.exe ~a" filename)))

(defun test ()
(paint (anti-alias (button :x0 10.0 :y0 10.0 :x1 190.0 :y1 60.0))))

justinhj

unread,
Feb 28, 2007, 1:55:44 PM2/28/07
to

Note: just realised you have to swap the order of the buzz print with
the fizz print to get the correct output ;)

Justin

Tim Bradshaw

unread,
Feb 28, 2007, 2:27:41 PM2/28/07
to
On 2007-02-28 17:38:48 +0000, Richard M Kreuter <kre...@progn.net> said:
>
> I think you mean "~&~[Fizz~:;~]~[Buzz~:;~D~]~%" right?

Probably, yes.

>
> (dotimes (i 100)
> (format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"
> (mod i 3) (mod i 5) i "Fizz" "Buzz"))
>

Really good, yes. Almost impossible to work out what it does. The
perfectionist in me would insist on

"~&~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"

- you do actually want that so you know it always starts a line
properly, apart from being an extra bit of squigglines. But that's
trivial compared to the main drag of it.

I'm unhappy about the explicit looping however. I can see two
solutions to this:

* something really gratuitous involving GO
* something equally gratuitous involving a lot of LAMBDAs.

I think I may be too rusty to write this, but something like:

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

--tim

Tim Bradshaw

unread,
Feb 28, 2007, 2:31:14 PM2/28/07
to
On 2007-02-28 06:31:05 +0000, rp...@rpw3.org (Rob Warnock) said:

> This one is both efficient -- *no* MOD calls at all! --
> *and* so ugly only a parent could love it: ;-} ;-}

I missed this. It's also really good. I think a combination of this
and (a working version of) the one I posted just now ought to be
something to be really proud of.

--tim

Ken Tilton

unread,
Feb 28, 2007, 2:55:26 PM2/28/07
to

Frank Buss wrote:
> Ken Tilton wrote:
>
>
>>You refer of course to the Cello code to create a 3-D oblong button of
>>variable thickness with rounded corners of variable radius:
>
>
> Yes, that's really ugly :-) Did you thought about using your dataflow
> paradigm to create graphics?

No. This will shock everyone and have Mastenbrook calling me a liar, but
my use of Cells does not violate Tilton's Law, viz., "Never use anything
for everything."

> Something similar would be to use higher order
> functions and combinators.

Look, I'm not smart like you guys, just a simple application programmer.
That is how I ended up working on paper and drawing pictures.

> The code below creates buttons like this:
>
> http://www.frank-buss.de/tmp/buttons.png

Wow, gorgeous. I let the graphicss card to the work, but your solution
is way cool.

Ken Tilton

unread,
Feb 28, 2007, 2:59:15 PM2/28/07
to

Ok, let's take another week on this, total up the cumulative man-months
and submit it for the 5-minute interview.

kt

ps. Me, I am not starting until we get the functional requirements
cleared up. k

Pillsy

unread,
Feb 28, 2007, 3:01:22 PM2/28/07
to
On Feb 28, 8:03 am, Ken Tilton <kentil...@gmail.com> wrote:
> Tim Bradshaw wrote:
[...]

> > Incidentally, I'm deeply disappointed in the quality of answers in
> > this thread. In the elder days there would have been at least a few
> > followups showing how to do this in the proper "FORMAT string
> > indistinguishable from line noise" way.

> Oh, absolutely, long overdue in this thread. Is this going to become a
> lost art? The village elders need to step up, methinks. I started
> playing with it, but I am just an elder, not a Lisp elder. Screams for a
> nested thingy, yes?

Well, if you're going to throw down the gauntlet like that, I'm
just going to have to respond in the hopes of provoking someone
into besting my wimpy attempt.

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

Cheers,
Pillsy


Richard M Kreuter

unread,
Feb 28, 2007, 3:22:24 PM2/28/07
to
Tim Bradshaw <t...@tfeb.org> writes:
> On 2007-02-28 17:38:48 +0000, Richard M Kreuter <kre...@progn.net> said:

> I'm unhappy about the explicit looping however. I can see two
> solutions to this:
>
> * something really gratuitous involving GO
> * something equally gratuitous involving a lot of LAMBDAs.

I'm sorry, but here's the best I can do for now.

(unwind-protect
(prog ((i 0) mod3 mod5)
(declare (special i mod3 mod5))
(setf (symbol-function 'cl-user::format-eval)
(lambda (stream thing colon atsign)
(declare (ignore stream colon atsign))
(handler-bind ((warning #'muffle-warning))
(eval thing))))
(setf mod3 (mod i 3)
mod5 (mod i 5))
format
(ecase
(catch 'go
(format
t
"~&~[~[~3@*~A~A~:;~3@*~A~*~]~:;~[~4@*~A~:;~D~2*~]~]~%~/CL-USER::FORMAT-EVAL/"
mod3 mod5 i "Fizz" "Buzz"
'(progn
(incf i)
(setf
mod3 (mod i 3)
mod5 (mod i 5))
(if (> i 100)
(throw 'go 'quit)
(throw 'go 'format)))))
(format (go format))
(quit (go quit)))
quit nil)
(fmakunbound 'cl-user::format-eval))

I know, it doesn't have enough LAMBDAs.

--
RmK

Harald Hanche-Olsen

unread,
Feb 28, 2007, 5:05:47 PM2/28/07
to
You want squiggles? How about

(format t "~&~:{ ~@[~A~]~@[~A~]~2:*~:[~:[~D~;~]~;~]~}~%"
(loop for n from 1 to 100
for fizz in '#3=(nil nil "Fizz" . #3#)
for buzz in '#5=(nil nil nil nil "Buzz" . #5#)
collect (list fizz buzz n)))

Or, if loop is for weenies, perhaps this one, with a few gratuitous
MAPs and NCONCs thrown in.

(format t "~&~:{ ~@[~A~]~@[~A~]~2:*~:[~:[~@[~C~]~C~;~]~;~]~}~%"
(cdr
(nconc
(map 'list
(lambda (ij f b) (cons f (cons b ij)))
(apply #'nconc
(map 'list
(lambda (i)
(map 'list
(lambda (j)
(list (if (eql i #\0) nil i) j))
#1="0123456789"))
#1#))
'#3=("Fizz" nil nil . #3#)
'#5=("Buzz" nil nil nil nil . #5#)))))

--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- It is undesirable to believe a proposition
when there is no ground whatsoever for supposing it is true.
-- Bertrand Russell

Harald Hanche-Olsen

unread,
Feb 28, 2007, 5:12:52 PM2/28/07
to
+ Harald Hanche-Olsen <han...@math.ntnu.no>:

| for fizz in '#3=(nil nil "Fizz" . #3#)
| for buzz in '#5=(nil nil nil nil "Buzz" . #5#)

But I hadn't read the whole thread and missed Rob Warnock's use of
this same trick in <zZSdnVArCY40vHjY...@speakeasy.net>.

Thomas A. Russ

unread,
Feb 28, 2007, 5:39:15 PM2/28/07
to
"Tim Bradshaw" <tfb+g...@tfeb.org> writes:
>
> Incidentally, I'm deeply disappointed in the quality of answers in
> this thread. In the elder days there would have been at least a few
> followups showing how to do this in the proper "FORMAT string
> indistinguishable from line noise" way. No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.

Well, OK -- I have gone into the West, but I stopped just short of the
Pacific Ocean. Obviously this requires some finesse since we don't want
to have to specify arguments more than once. So we do need to obscurely
move back and forth along the argument list:

(dotimes (i 20)
(format t "~[Fizz~:;~]~[Buzz~*~:;~2:*~[~2*~:;~*~D~]~]~%"
(mod i 3) (mod i 5) i))

Now this is just way too obscure for me -- and it looks vaguely like
Perl code ;)

My real preference for doing it using format would be for a solution
more like:

(dotimes (i 20)
(let ((divisible-by-3-p (zerop (mod i 3)))
(divisible-by-5-p (zerop (mod i 5))))
(format t "~@[Fizz~*~]~@[Buzz~*~]~:[~D~:;~*~]~%"
divisible-by-3-p divisible-by-5-p
(or divisible-by-3-p divisible-by-5-p)
i)))

Although for production code I would take one of the cond-based
programming solutions over trying to do this with format


--
Thomas A. Russ, USC/Information Sciences Institute

lojic

unread,
Feb 28, 2007, 9:34:03 PM2/28/07
to
On Feb 28, 3:24 am, Rainer Joswig <jos...@lisp.de> wrote:
> > (defun fizz-buzz (n)
> > (do ((i 1 (+ i 1))) ((> i n))
> > (let
> > ((fizz (zerop (mod i 3)))
> > (buzz (zerop (mod i 5))))
> > (when fizz (princ "Fizz"))
> > (when buzz (princ "Buzz"))
> > (format t "~A~%" (if (or fizz buzz) "" i)))))
>
> In DO you can have more than one clauses for iteration.
> So you could get rid of the extra LET.

Isn't let required to set the fizz and buzz local vars within the
loop?

Brian

lojic

unread,
Feb 28, 2007, 9:38:29 PM2/28/07
to
On Feb 28, 5:33 am, "Tim Bradshaw" <tfb+goo...@tfeb.org> wrote:
> On Feb 28, 2:02 am, job-271842...@craigslist.org wrote:
> > Is this a minority view? One of the things that attracted me to Lisp
> > was the simplicity, consistency, etc. of the language, so when I read
> > the above, it seemed reasonable.
>
> Simplicity? consistency? I think you're thinking of some other
> language there. CL is this vast industrial thing full of enormous
> machines, oil and rust.

I was referring to the syntax in particular e.g. (foo a b) and the
fact that the syntax makes it easier to generate and manipulate Lisp
code.

> Some compartments are full of water, and no
> one knows what some of the machines do, if anything. Many parts of it
> use a mixture of Whitworth & BSF threads (some left-handed), though
> much has now been converted to BA or metric, sometimes by use of taps
> & dies, sometimes with a hammer.
>
> CL's closest living relative is FORTRAN: always remember that.


>
> Incidentally, I'm deeply disappointed in the quality of answers in
> this thread. In the elder days there would have been at least a few
> followups showing how to do this in the proper "FORMAT string
> indistinguishable from line noise" way. No true CL programmer ever
> uses any other construct when the problem can be solved with a
> combination of FORMAT, LOOP & GO (FORMAT being always preferable,
> obviously). There may yet be those reading cll who know this, though
> I suspect they have all gone into the west now.
>

> --tim (who has used the FORMAT string mentioned inhttp://www.tfeb.org/lisp/obscurities.htmlin anger)


lojic

unread,
Feb 28, 2007, 9:52:44 PM2/28/07
to
On Feb 28, 10:51 am, "justinhj" <justi...@gmail.com> wrote:

The output should be:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
...

lojic

unread,
Feb 28, 2007, 10:19:08 PM2/28/07
to
On Feb 28, 5:39 pm, t...@sevak.isi.edu (Thomas A. Russ) wrote:
> Well, OK -- I have gone into the West, but I stopped just short of the
> Pacific Ocean. Obviously this requires some finesse since we don't want
> to have to specify arguments more than once. So we do need to obscurely
> move back and forth along the argument list:
>
> (dotimes (i 20)
> (format t "~[Fizz~:;~]~[Buzz~*~:;~2:*~[~2*~:;~*~D~]~]~%"
> (mod i 3) (mod i 5) i))
>
> Now this is just way too obscure for me -- and it looks vaguely like
> Perl code ;)

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

Brian

Rob Warnock

unread,
Feb 28, 2007, 10:55:58 PM2/28/07
to
Pillsy <pill...@gmail.com> wrote:
+---------------
+---------------

Very cute!! Except... it doesn't give correct answers:

1 2 Fizz 4 BuzzFizz 7 8 Buzz 10 Fizz12 13 FizzBuzz
15 16 Fizz 18 BuzzFizz 21 22 Buzz 24 Fizz26 27 FizzBuzz
29 30 Fizz 32 BuzzFizz 35 36 Buzz 38 Fizz40 41 FizzBuzz
43 44 Fizz 46 BuzzFizz 49 50 Buzz 52 Fizz54 55 FizzBuzz
57 58 Fizz 60 BuzzFizz 63 64 Buzz 66 Fizz68 69 FizzBuzz
71 72 Fizz 74 BuzzFizz 77 78 Buzz 80 Fizz82 83 FizzBuzz
85 86 Fizz 88 BuzzFizz 91 92 Buzz 94 Fizz96 97 FizzBuzz
99 100

- Those "BuzzFizz" should probably be "Buzz Fizz" (missing space).
- Missing spaces elsewhere, too.
- 9 is divisible by 3, not 5.
- 11 is *not* divisible by either 3 or 5.
- 12 *is* divisible by 3.
- 14 is *not* divisible by 3.
- Etc. etc...


-Rob

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

Rob Warnock

unread,
Feb 28, 2007, 11:10:35 PM2/28/07
to
Harald Hanche-Olsen <han...@math.ntnu.no> wrote:
+---------------

| + Harald Hanche-Olsen <han...@math.ntnu.no>:
| | for fizz in '#3=(nil nil "Fizz" . #3#)
| | for buzz in '#5=(nil nil nil nil "Buzz" . #5#)
|
| But I hadn't read the whole thread and missed Rob Warnock's use of
| this same trick in <zZSdnVArCY40vHjY...@speakeasy.net>.
+---------------

But yours is neater [== hackier], since it uses the actual non-NIL
generalized boolean values. Mine just used T & NIL in the circular
lists and had to provide the "Fizz" and "Buzz" elsewhere...

John Thingstad

unread,
Feb 28, 2007, 11:19:15 PM2/28/07
to

Shouldn't be necessary. The key to this cypher is in
the ANSI document under format options.

Vassil Nikolov

unread,
Feb 28, 2007, 11:21:59 PM2/28/07
to

On Wed, 28 Feb 2007 14:48:27 +0100, Rainer Joswig <jos...@lisp.de> said:
| ...
| Take this (not written by me).

Zmacs?

| ...
| How about this? This is clearly from aliens.

But was ported to Common Lisp at some point, wasn't it?

---Vassil.


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

Rob Warnock

unread,
Feb 28, 2007, 11:29:20 PM2/28/07
to
<nall...@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")))

lojic

unread,
Feb 28, 2007, 11:39:02 PM2/28/07
to
On Feb 28, 11:29 pm, r...@rpw3.org (Rob Warnock) wrote:
> 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")))

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

This was my off-the-cuff approach from a non-Lisp background. I would
like to see a macro-ized version and get some feedback from the group
regarding the appropriateness of macros in this case vs. a simple
function as in the above.

Brian Adkins

>
> -Rob
>
> -----
> Rob Warnock <r...@rpw3.org>

Rob Warnock

unread,
Feb 28, 2007, 11:52:13 PM2/28/07
to
Ken Tilton <kent...@gmail.com> wrote:
+---------------

| ps. Me, I am not starting until we get the functional requirements
| cleared up. k
+---------------

See my reply to <nall...@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 <rp...@rpw3.org>

Vassil Nikolov

unread,
Mar 1, 2007, 12:00:24 AM3/1/07
to

On Wed, 28 Feb 2007 19:27:41 +0000, Tim Bradshaw <t...@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.

Vassil Nikolov

unread,
Mar 1, 2007, 12:14:04 AM3/1/07
to

On 28 Feb 2007 12:01:22 -0800, "Pillsy" <pill...@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...

Ari Johnson

unread,
Mar 1, 2007, 12:16:38 AM3/1/07
to
rp...@rpw3.org (Rob Warnock) writes:

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

Vassil Nikolov

unread,
Mar 1, 2007, 1:24:09 AM3/1/07
to

On Wed, 28 Feb 2007 22:29:20 -0600, rp...@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.

Ken Tilton

unread,
Mar 1, 2007, 2:12:52 AM3/1/07
to

Vassil Nikolov wrote:
> On Wed, 28 Feb 2007 22:29:20 -0600, rp...@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.

Thank you! Finally someone has come to grips with the problem scalably.

I say we move into beta testing Q2 of '07 and production Q3.

Is this what the OE (original employer) was looking for?

hth,kt

Rainer Joswig

unread,
Mar 1, 2007, 3:30:36 AM3/1/07
to
In article <yy8virdl...@eskimo.com>,
Vassil Nikolov <vnikolo...@pobox.com> wrote:

> On Wed, 28 Feb 2007 14:48:27 +0100, Rainer Joswig <jos...@lisp.de> said:
> | ...
> | Take this (not written by me).
>
> Zmacs?
>
> | ...
> | How about this? This is clearly from aliens.
>
> But was ported to Common Lisp at some point, wasn't it?

Not sure. There are selected libraries that got ported.
But the system runs several Lisp dialects at the same time.

>
> ---Vassil.

themba....@gmail.com

unread,
Mar 1, 2007, 3:43:06 AM3/1/07
to
<snip>

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

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.

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)

1
"Burp"
"Fizz"
"Burp"
"Buzz"
"BurpFizz"
"Bang"
"Burp"
"Fizz"
"BurpBuzz"
11
"BurpFizz"
13
"BurpBang"
"FizzBuzz"
"Burp"

NIL
CL-USER>


Regards,

Themba

krew...@gmx.net

unread,
Mar 1, 2007, 5:16:16 AM3/1/07
to
On Mar 1, 6:00 am, Vassil Nikolov <vnikolov+use...@pobox.com> wrote:
> ((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).

Noobs allowed in this discussion? Well, I'll give it a try.
Using some previously posted ideas, I got rid of an explicit
loop by using:

(defvar *i* 0)
(defun foo (outstream format-string colon-p at-sign-p)
(declare (ignore format-string colon-p at-sign-p))
(prog1
(format outstream "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~
%"
(mod *i* 3) (mod *i* 5) *i* "Fizz" "Buzz")
(incf *i*)))

(defun fizzbuzz (n)
(let ((*i* 1))
(format t (format nil "~~~A@{~~/foo/~~:*~~}" n) nil)))

(fizzbuzz 15)

Total abuse of the language.

Albert

Rob Warnock

unread,
Mar 1, 2007, 5:32:52 AM3/1/07
to
lojic <lojic...@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 <nall...@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 <nall...@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 <rp...@rpw3.org>

Tim Bradshaw

unread,
Mar 1, 2007, 6:46:51 AM3/1/07
to
On Mar 1, 3:19 am, "lojic" <lojicdot...@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.

alex...@gmail.com

unread,
Mar 1, 2007, 7:28:58 AM3/1/07
to
On Mar 1, 2:32 am, nalle...@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

Ken Tilton

unread,
Mar 1, 2007, 8:46:36 AM3/1/07
to

Rob Warnock wrote:
> Ken Tilton <kent...@gmail.com> wrote:
> +---------------
> | ps. Me, I am not starting until we get the functional requirements
> | cleared up. k
> +---------------
>
> See my reply to <nall...@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.

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

Ken Tilton

unread,
Mar 1, 2007, 10:06:43 AM3/1/07
to

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.

Brian Adkins

unread,
Mar 1, 2007, 12:14:28 PM3/1/07
to
themba....@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?

Brian Adkins

unread,
Mar 1, 2007, 12:31:54 PM3/1/07
to

Thanks for the tips below. When I said, "non-Lisp background" I wasn't
referring to the Lispyness of the implementation of the function (which
I kind of slapped together with the tools I've accumulated through p.36
of Graham's book), but the use of a runtime function vs. a macro.

> Oh, sure, you might use

[tips deleted for brevity]

> +---------------
> | I would like to see a macro-ized version...
> +---------------
>
> Did you see <nall...@gmail.com>'s version for the case of a
> hard-coded '((3 "Fizz") (5 "Buzz")) list?

Actually no :( I searched for posts by nallen05 and didn't see any on
this thread, but Bellsouth had newsgroup server issues yesterday, they
may have dropped a few posts. I'll try searching Google groups for it.

> +---------------
> | ...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 <nall...@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].

Interesting

Pillsy

unread,
Mar 1, 2007, 3:04:07 PM3/1/07
to
On Mar 1, 12:14 am, Vassil Nikolov <vnikolov+use...@pobox.com> wrote:

> On 28 Feb 2007 12:01:22 -0800, "Pillsy" <pillsb...@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...

Good point!

(setf (symbol-value '|100|) 100)

(defun fb (stream arg colonp atsignp)
(declare (ignore arg colonp atsignp))
(incf *count*)
(format stream "~[Fizz~[Buzz~:;~]~^~:;~[Buzz~^~:;~D~]~]"
(mod *count* 3) (mod *count* 5) *count*))

(let ((*count* 0))
(format t "~v{~/fb/~%~}" |100| '#1=((). #1#)))

And I think this one actually works, too! :D

Cheers, Pillsy

Pillsy

unread,
Mar 1, 2007, 3:06:24 PM3/1/07
to

Well, as long as I copy-and-paste right, and don't leave the

(defvar *count*)

out!

Cheers,
Pillsy

Thomas Bakketun

unread,
Mar 1, 2007, 5:51:15 PM3/1/07
to
Richard M Kreuter wrote:

> Here's one that avoids the explicit FizzBuzz, and adds /seven/
> squiggles to my first stab:
>
> (dotimes (i 100)


> (format t "~[~[~3@*~A~A~:;~3@*~A~]~:;~[~4@*~A~:;~D~]~]~%"

> (mod i 3) (mod i 5) i "Fizz" "Buzz"))
>
> Better?

Is this the shortest version possible?

(defun fizz-buzz ()
(loop for i from 1 to 100 do
(format t "~&~[Fizz~]~[Buzz~:;~0@*~[~:;~*~A~]~]"
(mod i 3) (mod i 5) i)))

nall...@gmail.com

unread,
Mar 1, 2007, 8:21:15 PM3/1/07
to

ok, here ya go...

(defmacro def-fizz-buzz (name alist)
(let (l
(lcd (reduce #'* alist :key #'first)))
(do ((n 1 (incf n)))
((> n lcd))
(push '(terpri) l)
(let ((old l))
(dolist (a alist)
(when (zerop (mod n (first a)))
(push `(princ ,(rest a)) l)))
(when (eq l old)
(push '(princ y) l)))
(push '(when (> (incf y) x) (return)) l))
`(defun ,name (x)
(let ((y 1))
(loop ,@(nreverse l))))))


CL-USER> (macroexpand-1 '(def-fizz-buzz very-fizzy ((2 . "Burp") (3 .
"Fizz") (5 . "Buzz"))))
(DEFUN VERY-FIZZY (X)
(LET ((Y 1))
(LOOP (TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Buzz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Buzz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI) (PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI) (PRINC "Fizz")
(PRINC "Buzz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI) (PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Buzz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Buzz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Fizz")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC Y)


(WHEN (> (INCF Y) X) (RETURN))

(TERPRI)
(PRINC "Burp")
(PRINC "Fizz")
(PRINC "Buzz")
(WHEN (> (INCF Y) X) (RETURN)))))
T
CL-USER>

P.L.Hayes

unread,
Mar 1, 2007, 9:07:49 PM3/1/07
to
Thomas Bakketun <thomas-n...@bakketun.net> writes:


I think it would be if if it wasn't for the loop. When I had a go at
this at http://golf.shinh.org/p.rb?FizzBuzz the only way I could find
to get it down to the 90 characters others had managed was this:

(dotimes (i 101)
(format t "~[~:;~&~[Fizz~[Buzz~]~:;~[Buzz~:;~A~]~]~]"
i (mod i 3) (mod i 5) i))

[If you remove all unnecessary whitespace, it's 90 bytes long]

Cheers,
Paul.

Vassil Nikolov

unread,
Mar 1, 2007, 10:39:22 PM3/1/07
to

On 1 Mar 2007 12:06:24 -0800, "Pillsy" <pill...@gmail.com> said:
|| On Mar 1, 12:14 am, Vassil Nikolov <vnikolov+use...@pobox.com> wrote:
||
|| > On 28 Feb 2007 12:01:22 -0800, "Pillsy" <pillsb...@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...
||
|| Good point!
||
|| (setf (symbol-value '|100|) 100)

Er, I was objecting to the use of APPLY (see also CALL-ARGUMENTS-LIMIT).

Luke J Crook

unread,
Mar 1, 2007, 11:21:14 PM3/1/07
to
Frank Buss wrote:
> The code below creates buttons like this:
>
> http://www.frank-buss.de/tmp/buttons.png
>
> In LispWorks 4.3.7 it is not very fast, but SBCL needs not many seconds :-)
>

You know there is this little-known package called lispbuilder-sdl that
can also be used for the back-end rendering. ;)

- Luke

Brian Adkins

unread,
Mar 2, 2007, 12:18:52 AM3/2/07
to
P.L.Hayes wrote:
> I think it would be if if it wasn't for the loop. When I had a go at
> this at http://golf.shinh.org/p.rb?FizzBuzz the only way I could find
> to get it down to the 90 characters others had managed was this:

I'm extremely upset with you for posting that golf url! :)

I just wasted way too much time trying to squeeze my Ruby version down
and can't get below 71 bytes. I'd love to know how the top guy did it in
56. Sorry for being off topic with Ruby, but my Lisp chops aren't up to
the challenge yet:

1.upto(100){|i|puts"FizzBuzz#{i}"[i%3==0?0: i%5==0?4: 8,i%15==0?8: 4]}

I know there's a way to compute the substring indices mathematically
instead of logically, but I haven't found it yet.

Brian Adkins

unread,
Mar 2, 2007, 1:36:12 AM3/2/07
to
Brian Adkins wrote:
> P.L.Hayes wrote:
>> I think it would be if if it wasn't for the loop. When I had a go at
>> this at http://golf.shinh.org/p.rb?FizzBuzz the only way I could find
>> to get it down to the 90 characters others had managed was this:
>
> I'm extremely upset with you for posting that golf url! :)
>
> I just wasted way too much time trying to squeeze my Ruby version down
> and can't get below 71 bytes. I'd love to know how the top guy did it in
> 56. Sorry for being off topic with Ruby, but my Lisp chops aren't up to
> the challenge yet:
>
> 1.upto(100){|i|puts"FizzBuzz#{i}"[i%3==0?0: i%5==0?4: 8,i%15==0?8: 4]}

Got it down to 65 bytes: http://golf.shinh.org/p.rb?FizzBuzz
Something is definitely wrong when you get a bit of a thrill realizing
you can shave off a few bytes by transforming x==0 to x<1 in a few spots.

1.upto(100){|i|puts"FizzBuzz#{i}"[i%3<1?0:i%5<1?4:8,i%15<1?8:4]}

It is loading more messages.
0 new messages