{{collection|99 Bottles of Beer}} [[99 Bottles of Beer]] done in Lisp-languages



(defun bottles-of-beer (n)
   (if (zp n)
       (prog2$ (cw (concatenate 'string
                   "~N0 bottle~#1~[~/s~] of beer on the wall,~%"
                   "~n0 bottle~#1~[~/s~] of beer.~%"
                   "Take one down, pass it around,~%"
                   "~n2 bottle~#3~[~/s~] of beer on the wall.~%")
                   (if (= n 1) 0 1)
                   (1- n)
                   (if (= n 2) 0 1))
               (bottles-of-beer (- n 1)))))

Common Lisp

Sensible solution

(defun bottles (x)
  (loop for bottles from x downto 1
        do (format t "~a bottle~:p of beer on the wall
~:*~a bottle~:p of beer
Take one down, pass it around
~a bottle~:p of beer on the wall~2%" bottles (1- bottles))))

and then just call

(bottles 99)


(format t "~{~[~^~]~:*~D bottle~:P of beer on the wall~%~:*~D bottle~:P of beer~%Take one down, pass it around~%~D bottle~:P~:* of beer on the wall~2%~}"
          (loop :for n :from 99 :downto 0 :collect n))

The [http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm FORMAT function] is probably the most baroque (i.e. featureful almost to a fault) function in Common Lisp. To really drive this point home, try replacing each instance of ~D with ~R, and then with ~@R. Yes, this is all standard and dependable (dys?)functionality.

Explanation of the format string for the uninitiated:

  • ~{fmt~} expects the next argument to be a list (which is of the integers from 99 down to 0), and executes the format string fmt on each element. It is essentially a map or foreach.
  • ~[...~] is a case/switch. It executes the nth clause, where n is taken from the next argument. Since there is only one clause here, it will be executed only when the argument is 0.
  • ~^ will terminate formatting.
  • ~:* will back-up to the most-recently used argument.
  • ~D prints the next argument as a decimal number.
  • ~:P is for English plurals: it prints s if the last argument wasn't 1; it prints nothing otherwise. There's also ~@P for y/ies, in case you were worried about that. Note, by the way, how the emoticons :*~D and :P have shown up in the format string. FORMAT is so powerful, it's even self-aware about how silly it is.

Alternate solution

Bit of a beginner in Lisp, but this seems to work:

(defun beer-verse (count)
  "Recurses the verses"
  (format t "~A bottle~:P of beer on the wall~%" count)
  (format t "~A bottle~:P of beer~%" count)
  (format t "Take one down, pass it round~%")
  (format t "~A bottle~A of beer on the wall~%~%"
	  (if (= count 1)
	      (- count 1))
	  (if (/= count 2)
  (if (> count 1)
      (beer-verse (- count 1))))
(beer-verse 99)


(for (n 99 1)
(println n " bottles of beer on the wall," n " bottles of beer. Take one down, pass it around. ")
(println (- n 1) "bottles of beer on the wall!"))

;;also shows list afterword
(define (rec bottles)
	(if (!= 0 bottles) (print "/n" bottles " bottles of beer on the wall" bottles " bottles of beer.
\nTake one down, pass it around, " (- bottles 1)
" bottles of beer on the wall" (rec ( - bottles 1))))(list bottles))

(rec 99)


(define nn 99)

(for-each (lambda (n)
   (let ((bottle (lambda (n) (if (eq? n 1) " bottle" " bottles")))
         (m (- n 1)))
         n (bottle n) " of beer on the wall, "
         n (bottle n) " of beer." "\n"
         "Take one down and pass it around, "
         (if (eq? m 0) "no more" m)
         (bottle m) " of beer on the wall.\n")))
   (reverse (iota nn 1)))
   "No more bottles of beer on the wall, "
   "no more bottles of beer." "\n"
   "Go to the store and buy some more, "
   nn " bottles of beer on the wall.")


(de bottles (N)
   (case N
      (0 "No more beer")
      (1 "One bottle of beer")
      (T (cons N " bottles of beer")) ) )

(for (N 99 (gt0 N))
   (prinl (bottles N) " on the wall,")
   (prinl (bottles N) ".")
   (prinl "Take one down, pass it around,")
   (prinl (bottles (dec 'N)) " on the wall.")
   (prinl) )


(define bottles-h
  { number --> string }
  0 -> "No more beer"
  1 -> "One bottle of beer"
  N -> (make-string "~A bottles of beer" N))

(define bottles
  { number --> number }
  0 -> 0
  X -> (let Msg (bottles-h X)
         (do (output "~A on the wall~%~A~%Take one down, pass it around~%~A on the wall~%~%" Msg Msg (bottles-h (- X 1)))
             (bottles (- X 1)))))


def (beer n)
  when (n > 0)
    prn n " bottles of beer on the wall"
    prn n " bottles of beer"
    prn "take one down, pass it around"
    prn n-1 " bottles of beer on the wall"
    prn ""
    beer n-1