Attack of the Robots

Published on May 09, 2012

Powerful format

format又是一个在common lisp中灵活“过头”,争议超多的一个宏。它十分强大。

基本用法如下

(princ (reverse (format nil "Add onion rings for only ~$ dollars more!" 1.5))) 
;;;Control sequences for printing lisp values
;;(prin1 "foo")
(format t "I am printing ~s in the middle of this sentence" "foo")
;;(princ "foo")
(format t "I am printing ~a in the middle of this sentence" "foo")
;;第一个参数,右边补白
(format t "I am printing ~10a within ten spaces of room." "foo")
;;第二个参数,分成3部分,补白9格
(format t "I am printing ~10,3a within ten (or more) spaces of room." "foo")
;;第三个参数,右边补白4格
(format t "I am printing ~,,4a in the middle of this sentence." "foo")
;;第四个参数,补充符号
(format t "The world ~,,4,'!a feels very important." "foo")
(format t "The world ~,,4,'!@a feels very important." "foo")
;;;Control Sequences for formatting numbers
;;Integers
(format t "The number 1000 in hexadrcimal is ~x" 1000)
(format t "The number 1000 in hexadrcimal is ~b" 1000)
(format t "The number 1000 in hexadrcimal is ~d" 1000)
;digit group separators
(format t "Numbers with commas in them are ~:d times better." 1000000)
(format t "I am printing ~10d within ten spaces of room." 1000000)
(format t "I am printing ~10,'xd within ten spaces of room." 1000000)
;;;Control Sequences for formatting Floating-point numbers
;;default
(format t "PI can be estimated as ~4f" 3.141593)
;;小数点后数位
(format t "PI can be estimated as ~,4f" pi)
;;Percentage
(format t "Percentages are ~,,2f percent better than fractions" 0.77)
;;Format currency
(format t "I wish I had ~$ dollars in my bank account." 1000000.2)
;;;Print multiple lines of output
;;terpri
(progn (princ 22)
       (terpri)
       (princ 33))
;;fresh-line
(progn (princ 22)
       (fresh-line)
       (fresh-line)
       (princ 33))
;;~% like terpri
;;~& like fresh-line
(progn (format t "This is on one line ~%")
       (format t "~%This is on another line")) 
(progn (format t "This is on one line ~&")
       (format t "~&This is on another line"))
;;there can be a number in front of them
(format t "this will print ~5%on two lines spread far apart")
(format t "this will print ~5&on two lines spread far apart")
;;;Justifying output
;;for example a function
(defun random-animal ()
  (nth (random 5) '("dog" "tick" "tiger" "walrus" "kangaroo")))
;;equally width
(loop repeat 10
      do (format t "~5t~a ~15t~a ~25t~a~%"
                 (random-animal)
                 (random-animal)
                 (random-animal)))
;;equally apart
(loop repeat 10
      do (format t "~30<~a~;~a~;~a~>~%"
                 (random-animal)
                 (random-animal)
                 (random-animal)))
;;center
(loop repeat 10
      do (format t "~30:@<~a~>~%" (random-animal)))
(loop repeat 10
      do (format t "~30:@<~a~;~a~;~a~>~%"
                 (random-animal)
                 (random-animal)
                 (random-animal)))
;;still wavy?to make it like this
(loop repeat 10
      do (format t "~10:@<~a~>~10:@<~a~>~10:@<~a~>~%"
                 (random-animal)
                 (random-animal)
                 (random-animal)))
;;;Iterating Through Lists Using Control Sequences
;;Lets create a list of animals
(defparameter *animals* (loop repeat 10 collect (random-animal)))
;;loop through sequenses
(format t "~{I see a ~a! ~%~}" *animals*) 
;;not only one item
(format t "~{I see a ~a... or was it a ~a?~%~}" *animals*)
;;;A crazy formatting trick for creating pretty tables of data
(format t "|~{~<|~%|~,33:;~2d ~>~}|"
        (loop for x below 100 collect x))

可以看到,format的参数多的……而且像perl一样有很多“奇怪”的符号。

Attack by robots

让我们接下来看看一个被作者称作可怕的游戏,It's really drive me crazy!!

;;;整个界面大小为16x64,产生个1024长度序列
(defun robots ()
  (loop named main;便于跳出
     ;;方向
     with directions = '((q . -65) (w . -64) (e . -63) (a . -1)
                         (d .   1) (z .  63) (x .  64) (c . 65))
     ;;初始位置
     for pos = 544
     then (progn (format t "~%qwe/asd/zxc to move, (t)eleport, (l)eave:")
                 (force-output);clean any output not waiting return
                 (let* ((c (read))
                        (d (assoc c directions)))
                   (cond (d (+ pos (cdr d)))
                         ((eq 't c) (random 1024))
                         ((eq 'l c) (return-from main 'bye))
                         (t pos))))
     ;;获得monster的位置
     for monsters = (loop repeat 10
                          collect (random 1024))
     then (loop for mpos in monsters
                collect (if (> (count mpos monsters) 1)
                          mpos
                          ;;都走了一遍。
                          (cdar (sort (loop for (k . d) in directions
                                            for new-mpos = (+ mpos d)
                                            ;行与列距离和与新位置cons                                
                                            collect (cons (+ (abs (- (mod new-mpos 64) 
                                                                     (mod pos 64)))
                                                             (abs (- (ash new-mpos -6)
                                                                     (ash pos -6))))
                                                          new-mpos))
                                      '<
                                      :key #'car))))
     when (loop for mpos in monsters
                always (> (count mpos monsters) 1))
     return 'player-wins
     do (format t
                "~%|~{~<|~%|~,65:;~A~>~}|"
                (loop for p 
                      below 1024
                      collect (cond ((member p monsters) 
                                     (cond ((= p pos) (return-from main 'player-loses))
                                           ((> (count p monsters) 1) #\#)
                                           (t #\A)))
                                    ((= p pos) 
                                     #\@)
                                    (t 
                                     #\ ))))))

整个游戏就一个函数,由loop和format这种强大的lisp异类来完成。

写在最后

挺好玩的一个游戏。挺难理解的代码……

为了看懂又回头看看作者的loop周期表,再次见识了format和loop的强大之处,也感受到了它们的复杂。