在 Common Lisp 中,打印整数个别用函数format。例如,下面的代码会往规范输入中打印出233这个数字:

(format t "~D" 233)

除此之外,format还能够管制打印内容的宽度、填充字符、是否打印正负号等方面。例如,要管制打印的内容至多占据6列的话,能够用如下代码

(format t "~6D" 233)

如果不应用字符串模式的 DSL,而是以关键字参数的形式来实现一个可能达到同样成果的函数format-decimal,代码可能如下:

(defun format-decimal (n                       &key                         mincol)  "打印整数 N 到规范输入。MINCOL 如果不为 NIL,则示意所打印的内容至多要占据的列数。"  ;; 通过取余的形式失去 N 的每一位并一一入栈,之后出栈的程序就是从左到右打印的程序了。  (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 t "~6,'0D" 233)

format-decimal想要做到同样的事件,能够这么写:

(defun format-decimal (n                       &key                         mincol                         (padchar #\Space))  "打印整数 N 到规范输入。MINCOL 如果不为 NIL,则示意所打印的内容至多要占据的列数。PADCHAR 表达式为了填充多余的列时所用的字符。"  (check-type mincol (or integer null))  (check-type padchar character)  ;; 通过取余的形式失去 N 的每一位并一一入栈,之后出栈的程序就是从左到右打印的程序了。  (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中实现同样的性能

(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)           ;; 通过取余的形式失去 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)))    ;; 通过取余的形式失去 N 的每一位并一一入栈,之后出栈的程序就是从左到右打印的程序了。    (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

(format t "~9,'0@:D" 23333)

为此,给format-decimal新增一个关键字参数comma-separated来管制这一行为。

(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)           ;; 通过取余的形式失去 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)))    ;; 通过取余的形式失去 N 的每一位并一一入栈,之后出栈的程序就是从左到右打印的程序了。    (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个数字打印一个连字符

(format t "~9,'0,'-,4@:D" 23333)

对于format-decimal来说这个批改当初很简略了

(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)           ;; 通过取余的形式失去 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)))    ;; 通过取余的形式失去 N 的每一位并一一入栈,之后出栈的程序就是从左到右打印的程序了。    (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)

全文完。

浏览原文