format

;; Common Lisp 的 format 相当复杂,format 有 4 个输出的目的地:
;; - 标准输出(t)
;; - 带有填充指针的字符串
;; - 流
;; - nil(返回字符串)

;;; ~%,控制换行
(format t "hello~%world")
;; 输出:
;; hello
;; world

;;; 控制小数位数:
pi                                      ; => 3.141592653589793d0
;; 默认是小数后两位
(format nil "~$" pi)                    ; => "3.14"
;; 前置参数(prefix parameter)控制打印浮点数的位数
(format nil "~3$" pi)                   ; => "3.142"

;;; ~v,可以从参数列表中取值来做前置参数的值:
;;; 从参数列表中取“3”作为前置参数
(format t "~v$" 3 pi)                   ; => 3.142

;;; ~#,字符串参数之后的剩余参数个数将作为 ~# 的取值:
(format t "~#$" pi nil nil)             ; => 3.142

;;; ~:,让 ~D 输出的十进制数字以逗号分割:
(format t "~:d" 1000)                   ; => 1,000

;;; ~S 和 ~A 可将 NIL 输出成():
(format t "~:a" nil)                    ; => ()
(format t "~:S" nil)                    ; => ()

;;; ~c 可将不可打印的字符按名字输出:
(format t "~:C" #\Tab)                  ; => Tab

;;; ~@,给数字加上正负号:
(format t "~@d" -1000)                  ; => -1000
;;; 还可以和 ~: 结合:
(format t "~:@d" -1000)                 ; => -1,000
;;; 和 ~c 可将字符按字面字符打印出来:
(format t "~@C" #\a)                    ; => #\a

;;; ~&、~%,都是产生新的行,和 ~% 不同的是,~& 只有在没有位于一行开始处才换行。

;;; ~d,打印数字,支持前缀参数,第一个参数为输出的最小宽度:
(format nil "~10d" 100)                 ; => "       100"
;; 第二个参数指出数字之前的占位符号(默认是空格):
(format nil "~10,'0d" 100)              ; => "0000000100"
;; 第三个参数配合冒号,可以指定分割符:
(format nil "~,,'.:d" 1000)             ; => "1.000"
;; 第四个参数指定多少位才分割符,每一位就分割,其他都好都是做占位:
(format nil "~,,'|,1:d" 1000)           ; => "1|0|0|0"

;;; ~x, ~o, ~b,分别指十六进制、八进制和二进制,和 ~d 一样的使用方法

;;; ~r,控制 2~36 之间的进制,如二进制:
(format nil "~2r" 10)                   ; => "1010"

;;; ~r 有趣的地方在于:不给参数可以打印数字的英文单词:
(format nil "~r" 1)                     ; => "one"
(format nil "~r" 100)                   ; => "one hundred"
(format nil "~r" 1000)                  ; => "one thousand"
;; 如果加 ~:,可以让它成为序数:
(format nil "~:r" 1000)                 ; => "one thousandth"
;; 加 ~@ 可以让它成为罗马字符:
(format nil "~@r" 1)                    ; => "I"
;; 配合 ~: 可以让它产生旧式的罗马字符:
(format nil "~@r" 1234)                 ; => "MCCXXXIV"

;;; ~p,如果参数不是1,将打印个字符“s“,用来解决复数问题:
(format nil "~p" 0)                     ; => "s"
(format nil "~p" 1)                     ; => ""

;;; ~(,控制大小写:
;; 全部转成小写
(format nil "~(~A~)" "HELLO WORLD")     ; => "hello world"
;; 配合 ~@,让首字符大写:
(format nil "~@(~A~)" "HELLO WORLD")    ; => "Hello world"
;; 配合 ~:,让单词首字母大写:
(format nil "~:(~A~)" "HELLO WORLD")    ; => "Hello World"
;; ~: 结合 ~@,让所有字母大写:
(format nil "~:@(~A~)" "hello world")   ; => "HELLO WORLD"

;;; ~[~],按数值索引取内容:
(format t "~[1~;2~;~]" 0)               ; => 1
(format t "~[1~;2~;~]" 1)               ; => 2
;; 和 ~: 配合在一起,True 和 False 的条件选择:
(format t "~:[F~;T~]" 1)                ; => T
(format t "~:[F~;T~]" nil)              ; => F
;; 和 ~@ 配合在一起,可以只支持一条子句:
(format t "~@[x=~a~]~@[y=~a~]" nil nil) ; => NIL
(format t "~@[x=~a~]~@[y=~a~]" nil 1)   ; => y=1
(format t "~@[x=~a~]~@[y=~a~]" 1 2)     ; => x=1y=2

;;; ~{~},迭代,需要接受一个列表:
(format t "~{~a~%~}" (list 1 2 3 4))
;; 输出:
;; 1
;; 2
;; 3
;; 4
;; NIL
(format t "~{~a: ~a~%~}" (list 1 2 3 4))
;; 输出:
;; 1: 2
;; 3: 4
;; 这样看起来很讨厌,必须得让它在列表没元素的时候停止打印逗号:
(format t "~{~a,~}" (list 1 2 3 4 5))   ; => 1,2,3,4,5,

;;; ~^:
(format t "~{~a~^,~}" (list 1 2 3 4 5)) ; => 1,2,3,4,5
;; 配合 ~@,可以把参数当作列表处理:
(format t "~@{~a~^,~}" 1 2 3 4 5)       ; => 1,2,3,4,5