Common Lisp format常见用法

Common Lisp 中有两个不那么“lisp”的东西,其中一个是宏 loop,而另一个就是函数 format

format 用于构造字符串或是向 i/o 流输出字符串。

format

lisp format 的控制字符串不同于类 C 语言中的控制串,它比 C 中的控制串强大许多。对于用过正则表达式的人,大多都不会再用其他方法来处理字符串,这是因为正则表达式言简意赅,处理字符串高效。而基于 lisp format 语言的控制字符串在格式化字符上,就有正则表达式般的功效,简洁而高效。

来自 Practical Common Lisp 的例子,如下。

定义列表:

(defparameter *x* (list 1 3 5 7))

以结构化编程的思维,如果要遍历该 list,可以用 loop:

(loop for cons on *x*
    do (format t "~a" (car cons))
    when (cdr cons) do (format t ", "))
; 1, 3, 5, 7

然而如果用 format 格式化字符串,可以更简洁高效:

(format t "~{~a~^, ~}" *x*)
; 1, 3, 5, 7

一般格式化

常见的格式化字符“~$, ~a, ~d, ~f, ~&, ~%, ~~”的用例如下:

(format t "~$" pi)        ; 3.14 -> NIL
(format t "~5$" pi)       ; 3.14159

(format t "~v$" 3 pi)     ; 3.142
(format t "~#$" pi)       ; 3.1

(format t "~,5f" pi)      ; 3.14159
(format t "~5f" pi)       ; 3.142

(format t "~d" 100000)    ; 100000
(format t "~f" 100000)    ; 100000.0

(format t "~:d" 100000)   ; 100,000
(format t "~@d" 100000)   ; +100000

(format t "~:@d" 100000)  ; +100,000
(format t "~@:d" 100000)  ; +100,000

(format t "~:a" 100000)   ; 100000
(format t "~a" 100000)    ; 100000

(format t "~a~&" 100000.3)   ; 100000.3
(format t "~a~%" 100000.3)   ; 100000.3
(format t "~a~&~&" 100000.3) ; 100000.3
(format t "~a~%~%" 100000.3) ; 100000.3n

(format t "~a~~" 100000.3)   ; 100000.3~
(format t "~a~3~" 100000.3)  ; 100000.3~~~

字符与浮点数

同样摘自 Practical Common Lisp,下例展示了字符与浮点数的格式化:

(format t "~@c~%" #a)  ; #a
(format t "~c~%" #a)   ; a
(format t "~:c~%" #a)  ; a
(format t "~@:c~%" #a) ; a

(format nil "~:@c" (code-char 0))   ; -> "Null"
(format nil "~:@c" (code-char 1))   ; -> "Soh"
(format nil "~:@c" (code-char 2))   ; -> "Stx"
(format nil "~:@c" (code-char 3))   ; -> "Etx"
(format nil "~:@c" (code-char 4))   ; -> "Eot"
(format nil "~:@c" (code-char 5))   ; -> "Enq"
(format nil "~:@c" (code-char 6))   ; -> "Ack"
(format nil "~:@c" (code-char 7))   ; -> "Bell"
(format nil "~:@c" (code-char 8))   ; -> "Backspace"
(format nil "~:@c" (code-char 9))   ; -> "Tab"

(format nil "~:@d" 1000000)         ; -> "+1,000,000"
(format nil "~12d" 1000000)         ; -> "     1000000"
(format nil "~12,'0d" 1000000)      ; -> "000001000000"

(format nil "~4,'0d-~2,'0d-~2,'0d" 2018 2 22) ; -> "2018-02-22"

(format nil "~:d" 1000000000)       ; -> "1,000,000,000"
(format nil "~,,'.,4:d" 1000000000) ; -> "10.0000.0000"

(format nil "~x" 1000000000)        ; -> "3B9ACA00"
(format nil "~o" 1000000000)        ; -> "7346545000"
(format nil "~b" 1000000000)        ; -> "111011100110101100101000000000"

(format nil "~f" pi)                ; -> "3.1415926535897932385"
(format nil "~,4f" pi)              ; -> "3.1416"
(format nil "~e" pi)                ; -> "3.1415926535897932385L+0"
(format nil "~,4e" pi)              ; -> "3.1416L+0"
(format nil "~$" pi)                ; -> "3.14"
(format nil "~2,4$" pi)             ; -> "0003.14"

英语化输出

包括数字口语化、单复数变换等,见下例:

(format nil "~r" 1234)        ; -> "one thousand, two hundred and thirty-four"
(format nil "~:r" 1234)       ; -> "one thousand, two hundred thirty-fourth"
(format nil "~@r" 1234)       ; -> "MCCXXXIV"
(format nil "~:@r" 1234)      ; -> "MCCXXXIIII"

(format nil "~:@r" 4)         ; -> "IIII"
(format nil "~@r" 4)          ; -> "IV"
(format nil "~:@r" 9)         ; -> "VIIII"
(format nil "~@r" 9)          ; -> "IX"

(format nil "file~p" 1)       ; -> "file"
(format nil "file~p" 10)      ; -> "files"
(format nil "file~p" 0)       ; -> "files"
(format nil "file~p" -1)      ; -> "files"

(format nil "~r file~:p" 1)   ; -> "one file"
(format nil "~r file~:p" 10)  ; -> "ten files"
(format nil "~r file~:p" 0)   ; -> "zero files"

(format nil "~r famil~:@p" 1) ; ->  "one family"
(format nil "~r famil~:@p" 10); -> "ten families"
(format nil "~r famil~:@p" 0) ; -> "zero families"

(format nil "~(~a~)" "FOO")   ; -> "foo"
(format nil "~(~@a~)" 123)    ; -> "123"
(format nil "~(~@r~)" 123)    ; -> "cxxiii"

(format nil "~(~a~)" "tHe Quick BROWN foX")   ; -> "the quick brown fox"
(format nil "~@(~a~)" "tHe Quick BROWN foX")  ; -> "The quick brown fox"
(format nil "~:(~a~)" "tHe Quick BROWN foX")  ; -> "The Quick Brown Fox"
(format nil "~:@(~a~)" "tHe Quick BROWN foX") ; -> "THE QUICK BROWN FOX"

条件输出

使用简单的判断逻辑来输出字符串,见下例:

(format nil "~[cero~;uno~;dos~]" 0)     ; -> "cero"
(format nil "~[cero~;uno~;dos~]" 1)     ; -> "uno"
(format nil "~[cero~;uno~;dos~]" 2)     ; -> "dos"
(format nil "~[cero~;uno~;dos~]" 3)     ; -> ""

(format nil "~[cero~;uno~:;dos~]" 3)    ; -> "dos"
(format nil "~[cero~;uno~:;dos~]" 100)  ; -> "dos"

(defparameter *list-etc*
  "~#[NONE~;~a~;~a and ~a~:;~a, ~a~]~#[~; and ~a~:;, ~a, etc~].")
(format nil *list-etc*)                 ; -> "NONE."
(format nil *list-etc* 'a)              ; -> "A."
(format nil *list-etc* 'a 'b)           ; -> "A and B."
(format nil *list-etc* 'a 'b 'c)        ; -> "A, B and C."
(format nil *list-etc* 'a 'b 'c 'd)     ; -> "A, B, C, etc."
(format nil *list-etc* 'a 'b 'c 'd 'e)  ; -> "A, B, C, etc."

(format nil "~@[x = ~a ~]~@[y = ~a~]" 10 20)    ; -> "x = 10 y = 20"
(format nil "~@[x = ~a ~]~@[y = ~a~]" 10 nil)   ; -> "x = 10 "
(format nil "~@[x = ~a ~]~@[y = ~a~]" nil 20)   ; -> "y = 20"
(format nil "~@[x = ~a ~]~@[y = ~a~]" nil nil)  ; -> ""

迭代输出

迭代主要针对 list 的格式化,见下例:

(format nil "~{~a, ~}" (list 1 2 3))    ; -> "1, 2, 3, "
(format nil "~{~a~^, ~}" (list 1 2 3))  ; -> "1, 2, 3"

(format nil "~@{~a~^, ~}" (list 1 2 3)) ; -> "(1 2 3)"
(format nil "~@{~a~^, ~}" 1 2 3)        ; -> "1, 2, 3"

(format nil "~{~a~#[~;, and ~:;, ~]~}" (list 1 2 3))  ; -> "1, 2, and 3"
(format nil "~{~a~#[~;, and ~:;, ~]~}" (list 1 2))    ; -> "1, and 2"

(defparameter *english-list*
  "~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}")
(format nil *english-list* '())         ; -> ""
(format nil *english-list* '(1))        ; -> "1"
(format nil *english-list* '(1 2))      ; -> "1 and 2"
(format nil *english-list* '(1 2 3))    ; -> "1, 2, and 3"
(format nil *english-list* '(1 2 3 4))  ; -> "1, 2, 3, and 4"

跳跃

跳跃指令为“~*”,用于在格式化参数列表中跳跃,见下例:

(format nil "~r ~:*(~d)" 1)                       ; -> "one (1)"

(format nil "I saw ~r el~:*~[ves~;f~:;ves~]." 0)  ; -> "I saw zero elves."
(format nil "I saw ~r el~:*~[ves~;f~:;ves~]." 1)  ; -> "I saw one elf."
(format nil "I saw ~r el~:*~[ves~;f~:;ves~]." 2)  ; -> "I saw two elves."

(format nil "~{~s~*~^ ~}" '(:a 10 :b 20))         ; -> ":A :B"

(format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 0) ; -> "I saw no elves."
(format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 1) ; -> "I saw one elf."
(format nil "I saw ~[no~:;~:*~r~] el~:*~[ves~;f~:;ves~]." 2) ; -> "I saw two elves."

小结

以上是常见的 format 格式字符串用例,然而 Common Lisp format 还有许多更高级的用法,这里就没有整理了。

参考:

  1. Practical Common Lisp

作者: YanWen

Web 开发者

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

You are commenting using your WordPress.com account. Log Out /  更改 )

Google photo

You are commenting using your Google account. Log Out /  更改 )

Twitter picture

You are commenting using your Twitter account. Log Out /  更改 )

Facebook photo

You are commenting using your Facebook account. Log Out /  更改 )

Connecting to %s