在 Common Lisp 中,打印整数一般用函数format
。例如,上面的代码会往标准输出中打印出233这个数字:
除此之外,format
还可以控制打印内容的宽度、填充字符、是否打印正负号等方面。例如,要控制打印的内容至少占据6列的话,可以用如下代码
如果不使用字符串形式的 DSL,而是以关键字参数的方式来实现一个能够达到同样效果的函数format-decimal
,代码可能如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| (defun format-decimal (n &key mincol) "打印整数 N 到标准输出。
MINCOL 如果不为 NIL,则表示所打印的内容至少要占据的列数。" (let ((digits '())) (cond ((zerop n) (push 0 digits)) (t (do ((n n (truncate n 10))) ((zerop n)) (push (rem n 10) digits)))) (when (and (integerp mincol) (> mincol (length digits))) (dotimes (i (- mincol (length digits))) (declare (ignorable i)) (princ #\Space)))
(dolist (digit digits) (princ (code-char (+ digit (char-code #\0)))))))
(format-decimal 233 :mincol 6)
|
如果要求用数字0而不是空格来填充左侧的列,用format
的写法如下:
format-decimal
想要做到同样的事情,可以这么写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| (defun format-decimal (n &key mincol (padchar #\Space)) "打印整数 N 到标准输出。
MINCOL 如果不为 NIL,则表示所打印的内容至少要占据的列数。 PADCHAR 表达式为了填充多余的列时所用的字符。" (check-type mincol (or integer null)) (check-type padchar character) (let ((digits '())) (cond ((zerop n) (push 0 digits)) (t (do ((n n (truncate n 10))) ((zerop n)) (push (rem n 10) digits)))) (when (and (integerp mincol) (> mincol (length digits))) (dotimes (i (- mincol (length digits))) (declare (ignorable i)) (princ padchar)))
(dolist (digit digits) (princ (code-char (+ digit (char-code #\0)))))))
(format-decimal 233 :mincol 6 :padchar #\0)
|
-D
默认是不会打印非负整数的符号的,可以用修饰符@
来修改这个行为。例如,(format t "~6,'0@D" 233)
会打印出00+233
。稍微修改一下就可以在format-decimal
中实现同样的功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| (defun format-decimal (n &key mincol (padchar #\Space) signed) "打印整数 N 到标准输出。
MINCOL 如果不为 NIL,则表示所打印的内容至少要占据的列数。 PADCHAR 表达式为了填充多余的列时所用的字符。" (check-type mincol (or integer null)) (check-type padchar character) (flet ((to-digits (n) (let ((digits '())) (cond ((zerop n) (push #\0 digits)) (t (do ((n n (truncate n 10))) ((zerop n)) (push (code-char (+ (rem n 10) (char-code #\0))) digits)))) digits))) (let ((digits (to-digits (abs n)))) (when (or signed (< n 0)) (push (if (< n 0) #\- #\+) digits)) (when (and (integerp mincol) (> mincol (length digits))) (dotimes (i (- mincol (length digits))) (declare (ignorable i)) (princ padchar)))
(dolist (digit digits) (princ digit)))))
(format-decimal 233 :mincol 6 :padchar #\0 :signed t)
|
除了@
之外,:
也是一个~D
的修饰符,它可以让format
每隔3个数字就打印出一个逗号,方便阅读比较长的数字。例如,下列代码会打印出00+23,333
:
1
| (format t "~9,'0@:D" 23333)
|
为此,给format-decimal
新增一个关键字参数comma-separated
来控制这一行为。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| (defun format-decimal (n &key comma-separated mincol (padchar #\Space) signed) "打印整数 N 到标准输出。
COMMA-SEPARATED 如果为 T,则每打印3个字符就打印一个逗号。 MINCOL 如果不为 NIL,则表示所打印的内容至少要占据的列数。 PADCHAR 表示填充多余的列时所用的字符。 SIGNED 控制是否显示非负整数的加号。" (check-type comma-separated boolean) (check-type mincol (or integer null)) (check-type padchar character) (check-type signed boolean) (flet ((to-digits (n) (let ((digits '())) (cond ((zerop n) (push #\0 digits)) (t (do ((count 0 (1+ count)) (n n (truncate n 10))) ((zerop n)) (when (and comma-separated (> count 0) (zerop (rem count 3))) (push #\, digits)) (push (code-char (+ (rem n 10) (char-code #\0))) digits)))) digits))) (let ((digits (to-digits (abs n)))) (when (or signed (< n 0)) (push (if (< n 0) #\- #\+) digits)) (when (and (integerp mincol) (> mincol (length digits))) (dotimes (i (- mincol (length digits))) (declare (ignorable i)) (princ padchar)))
(dolist (digit digits) (princ digit)))))
(format-decimal -23333 :comma-separated t :mincol 9 :padchar #\0 :signed t)
|
事实上,打印分隔符的步长,以及作为分隔符的逗号都是可以定制的。例如,可以改为每隔4个数字打印一个连字符
1
| (format t "~9,'0,'-,4@:D" 23333)
|
对于format-decimal
来说这个修改现在很简单了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| (defun format-decimal (n &key (commachar #\,) (comma-interval 3) comma-separated mincol (padchar #\Space) signed) "打印整数 N 到标准输出。
COMMACHAR 表示当需要打印分隔符时的分隔符。 COMMA-INTERVAL 表示当需要打印分隔符时需要间隔的步长。 COMMA-SEPARATED 如果为 T,则每打印3个字符就打印一个逗号。 MINCOL 如果不为 NIL,则表示所打印的内容至少要占据的列数。 PADCHAR 表示填充多余的列时所用的字符。 SIGNED 控制是否显示非负整数的加号。" (check-type commachar character) (check-type comma-interval integer) (check-type comma-separated boolean) (check-type mincol (or integer null)) (check-type padchar character) (check-type signed boolean) (flet ((to-digits (n) (let ((digits '())) (cond ((zerop n) (push #\0 digits)) (t (do ((count 0 (1+ count)) (n n (truncate n 10))) ((zerop n)) (when (and comma-separated (> count 0) (zerop (rem count comma-interval))) (push commachar digits)) (push (code-char (+ (rem n 10) (char-code #\0))) digits)))) digits))) (let ((digits (to-digits (abs n)))) (when (or signed (< n 0)) (push (if (< n 0) #\- #\+) digits)) (when (and (integerp mincol) (> mincol (length digits))) (dotimes (i (- mincol (length digits))) (declare (ignorable i)) (princ padchar)))
(dolist (digit digits) (princ digit)))))
(format-decimal -23333 :commachar #\- :comma-interval 4 :comma-separated t :mincol 9 :padchar #\0 :signed t)
|
全文完。