在使用递归连接函数格式后,在 Common Lisp 中打印字符串
Printing a string in Common Lisp, after concatening function format with recursion
我正在尝试学习 Common Lisp,阅读 Paul Graham 的 Ansi Common Lisp 和使用 EEC325 course 评论和 运行-测试函数和讲座。我用 slime 和 SBCL
设置了 Emacs
问题在第 3 章练习 8 中说:
Define a function that takes a list and prints it in dot notation:
> (showdots ' ( a b c))
(A . (B . (C . NIL)))
NIL
我做了以下函数,结果是一个字符串,它适用于案例,但不打印这是练习的主要 objective
(defun show-dots (lst)
(cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst))))))
问题是它生成的字符串不打印字符串但它有效
CS325-USER> (SHOW-DOTS '(A B C))
"(A . (B . (C . NIL)))"
CS325-USER> (SHOW-DOTS '(A (B C)))
"(A . ((B . (C . NIL)) . NIL))"
CS325-USER> (SHOW-DOTS '(A . B))
"(A . B)"
CS325-USER> (SHOW-DOTS NIL)
"NIL"
CS325-USER> (SHOW-DOTS '(NIL))
"(NIL . NIL)"
测试需要打印,所以它确实失败了,但很明显问题出现在打印结果时
(run-tests show-dots)
SHOW-DOTS: (SHOW-DOTS '(A B C)) failed:
Should have printed "(A . (B . (C . NIL)))" but saw ""
SHOW-DOTS: (SHOW-DOTS '(A (B C))) failed:
Should have printed "(A . ((B . (C . NIL)) . NIL))" but saw ""
SHOW-DOTS: (SHOW-DOTS '(A . B)) failed:
Should have printed "(A . B)" but saw ""
SHOW-DOTS: (SHOW-DOTS NIL) failed:
Should have printed "NIL" but saw ""
SHOW-DOTS: (SHOW-DOTS '(NIL)) failed:
Should have printed "(NIL . NIL)" but saw ""
SHOW-DOTS: 0 assertions passed, 5 failed.
所以我认为我必须做的唯一一件事就是在创建之后打印这个字符串,但是这个东西不起作用,我不明白为什么
1) 尝试
(defun show-dots (lst)
(cond
((atom lst) (format t "~A" lst ))
((consp lst) (format t "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst))))))
有了这个结果
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我说好的,这是疯狂的第一个字符串并打印它
(defun show-dots (lst)
(format t (cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst)))))))
但结果不正确
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我说好的,让我们创建一个局部变量,将字符串放在那里并打印它,但它再次不起作用
(defun show-dots (lst)
(let ((str (cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst)))))))
(format t str)))
结果不正确
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我真的很想了解这里发生了什么,也许有些愚蠢,但我不明白这一点。
谢谢你的时间
如果返回字符串确实是问题说明的一部分,则使用 CONCATENATE 'STRING
。如果没有,甚至不用理会它,只需坚持 FORMAT T
并打印到控制台即可。
你生成字符串的原始函数实际上非常接近。问题在于,如果要递归调用生成字符串的函数,则不应打印该字符串,因为您不希望也打印中间字符串。您可以做的一个非常简单的更改是使 show-dots 函数的主体成为创建字符串的内部 helper 函数,并且然后在主函数中,打印辅助函数的结果:
(defun show-dots (lst)
(labels ((%show-dots (lst)
(cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(%show-dots (car lst))
(%show-dots (cdr lst)))))))
(write-string (%show-dots lst))
nil))
CL-USER> (show-dots '(a b c))
(A . (B . (C . NIL)))
NIL
另一种方法是使用可选参数来指示字符串是应该打印还是 returned,它可以默认打印,但在递归情况下,你会 return 取而代之。实际上,由于 format 将 t 和 nil 作为具有这些语义的输出参数,您可以非常偷偷摸摸:
(defun show-dots (lst &optional (output t))
;; If OUTPUT is T (the default) then stuff will actually be printed,
;; and FORMAT returns NIL. If OUTPUT is NIL (as it is in the
;; recursive calls), then FORMAT creates the string and returns it,
(cond
((atom lst) (format output "~A" lst))
((consp lst) (format output "(~A . ~A)"
(show-dots (car lst) nil)
(show-dots (cdr lst) nil)))))
CL-USER> (show-dots '(a b c))
(A . (B . (C . NIL)))
NIL
也就是说,这两种实现最终都会创建一堆中间字符串,然后将它们连接在一起。这不是 space 的有效使用。在遍历正在打印的对象时写入流可能会更好。也许最直接的方法是自己处理括号和点的格式。这将导致或多或少像这样的解决方案(它 returns nil 因为那是你给出的第一个例子所做的):
(defun print-dotted (object &optional (stream *standard-output*))
"Print the object as usual, unless it is a cons, in which case
always print it in dotted notation. Return NIL."
(prog1 nil
(cond
;; write non-conses with WRITE
((not (consp object))
(write object :stream stream))
;; write the "(" " . " and ")" by hand,
;; and call print-dotted recursively for
;; the car and the cdr.
(t (write-char #\( stream)
(print-dotted (car object) stream)
(write-string " . " stream)
(print-dotted (cdr object) stream)
(write-char #\) stream)))))
CL-USER> (print-dotted '(a b c))
(A . (B . (C . NIL)))
;=> NIL
现在,format 函数实际上可以使用 tilde slash 指令调用以格式字符串命名的其他函数。这意味着你可以做这样的事情,我认为这有点优雅(我定义了一个新包,只是为了说明波浪线格式可以在其他包中查找符号;如果你在 CL-USER 中做事,你可以忽略它):
(defpackage ex
(:use "COMMON-LISP"))
(in-package #:ex)
(defun dot-cons (stream object &rest args)
(declare (ignore args))
(if (consp object)
(format stream "(~/ex:dot-cons/ . ~/ex:dot-cons/)" (car object) (cdr object))
(write object :stream stream)))
CL-USER> (format t "~/ex:dot-cons/" '(a b c))
(A . (B . (C . NIL)))
;=> NIL
试着理解它的作用:
CL-USER 9 > (format t "(~a ~a)" (princ 1) (princ 2))
12(1 2)
NIL
FORMAT
是一个函数。首先评估参数。 (princ 1)
得到评估。它打印 1
和 returns 1
。然后 (princ 2)
被评估。它打印 2
和 returns 2
。函数 FORMAT
被调用时使用评估的参数:t
、"(~a ~a)"
、1
和 2
。它打印 (1 2)
和 returns NIL
.
现在看这个:
CL-USER 8 > (progn (princ "(")
(princ 1)
(princ " . ")
(princ 2)
(princ ")"))
(1 . 2)
")"
打印 cons 单元格时重复使用上面的内容:
CL-USER 10 > (defun princme (c)
(if (consp c)
(progn
(princ "(")
(princme (car c))
(princ " . ")
(princme (cdr c))
(princ ")"))
(princ c)))
PRINCME
CL-USER 11 > (princme '(1 2 3))
(1 . (2 . (3 . NIL)))
注意:你原来的递归show-dots
生成字符串不是一个好主意。为什么?因为它递归地处理字符串并可能产生大量垃圾...
CL-USER 14 > (trace show-dots)
(SHOW-DOTS)
CL-USER 15 > (show-dots '((1 2) (3 (4 (5 6) ))))
0 SHOW-DOTS > ...
>> LST : ((1 2) (3 (4 (5 6))))
1 SHOW-DOTS > ...
>> LST : (1 2)
2 SHOW-DOTS > ...
>> LST : 1
2 SHOW-DOTS < ...
<< VALUE-0 : "1"
2 SHOW-DOTS > ...
>> LST : (2)
3 SHOW-DOTS > ...
>> LST : 2
3 SHOW-DOTS < ...
<< VALUE-0 : "2"
3 SHOW-DOTS > ...
>> LST : NIL
3 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
2 SHOW-DOTS < ...
<< VALUE-0 : "(2 . NIL)"
1 SHOW-DOTS < ...
<< VALUE-0 : "(1 . (2 . NIL))"
1 SHOW-DOTS > ...
>> LST : ((3 (4 (5 6))))
2 SHOW-DOTS > ...
>> LST : (3 (4 (5 6)))
3 SHOW-DOTS > ...
>> LST : 3
3 SHOW-DOTS < ...
<< VALUE-0 : "3"
3 SHOW-DOTS > ...
>> LST : ((4 (5 6)))
4 SHOW-DOTS > ...
>> LST : (4 (5 6))
5 SHOW-DOTS > ...
>> LST : 4
5 SHOW-DOTS < ...
<< VALUE-0 : "4"
5 SHOW-DOTS > ...
>> LST : ((5 6))
6 SHOW-DOTS > ...
>> LST : (5 6)
7 SHOW-DOTS > ...
>> LST : 5
7 SHOW-DOTS < ...
<< VALUE-0 : "5"
7 SHOW-DOTS > ...
>> LST : (6)
8 SHOW-DOTS > ...
>> LST : 6
8 SHOW-DOTS < ...
<< VALUE-0 : "6"
8 SHOW-DOTS > ...
>> LST : NIL
8 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
7 SHOW-DOTS < ...
<< VALUE-0 : "(6 . NIL)"
6 SHOW-DOTS < ...
<< VALUE-0 : "(5 . (6 . NIL))"
6 SHOW-DOTS > ...
>> LST : NIL
6 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
5 SHOW-DOTS < ...
<< VALUE-0 : "((5 . (6 . NIL)) . NIL)"
4 SHOW-DOTS < ...
<< VALUE-0 : "(4 . ((5 . (6 . NIL)) . NIL))"
4 SHOW-DOTS > ...
>> LST : NIL
4 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
3 SHOW-DOTS < ...
<< VALUE-0 : "((4 . ((5 . (6 . NIL)) . NIL)) . NIL)"
2 SHOW-DOTS < ...
<< VALUE-0 : "(3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL))"
2 SHOW-DOTS > ...
>> LST : NIL
2 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
1 SHOW-DOTS < ...
<< VALUE-0 : "((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL)"
0 SHOW-DOTS < ...
<< VALUE-0 : "((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
"((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
所有对 FORMAT
的调用都在分配新字符串...
更好:
CL-USER 16 > (with-output-to-string (*standard-output*)
(princme '((1 2) (3 (4 (5 6) )))))
"((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
通常考虑流输出,而不是字符串连接。
我正在尝试学习 Common Lisp,阅读 Paul Graham 的 Ansi Common Lisp 和使用 EEC325 course 评论和 运行-测试函数和讲座。我用 slime 和 SBCL
设置了 Emacs问题在第 3 章练习 8 中说:
Define a function that takes a list and prints it in dot notation:
> (showdots ' ( a b c)) (A . (B . (C . NIL))) NIL
我做了以下函数,结果是一个字符串,它适用于案例,但不打印这是练习的主要 objective
(defun show-dots (lst)
(cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst))))))
问题是它生成的字符串不打印字符串但它有效
CS325-USER> (SHOW-DOTS '(A B C))
"(A . (B . (C . NIL)))"
CS325-USER> (SHOW-DOTS '(A (B C)))
"(A . ((B . (C . NIL)) . NIL))"
CS325-USER> (SHOW-DOTS '(A . B))
"(A . B)"
CS325-USER> (SHOW-DOTS NIL)
"NIL"
CS325-USER> (SHOW-DOTS '(NIL))
"(NIL . NIL)"
测试需要打印,所以它确实失败了,但很明显问题出现在打印结果时
(run-tests show-dots)
SHOW-DOTS: (SHOW-DOTS '(A B C)) failed:
Should have printed "(A . (B . (C . NIL)))" but saw ""
SHOW-DOTS: (SHOW-DOTS '(A (B C))) failed:
Should have printed "(A . ((B . (C . NIL)) . NIL))" but saw ""
SHOW-DOTS: (SHOW-DOTS '(A . B)) failed:
Should have printed "(A . B)" but saw ""
SHOW-DOTS: (SHOW-DOTS NIL) failed:
Should have printed "NIL" but saw ""
SHOW-DOTS: (SHOW-DOTS '(NIL)) failed:
Should have printed "(NIL . NIL)" but saw ""
SHOW-DOTS: 0 assertions passed, 5 failed.
所以我认为我必须做的唯一一件事就是在创建之后打印这个字符串,但是这个东西不起作用,我不明白为什么
1) 尝试
(defun show-dots (lst)
(cond
((atom lst) (format t "~A" lst ))
((consp lst) (format t "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst))))))
有了这个结果
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我说好的,这是疯狂的第一个字符串并打印它
(defun show-dots (lst)
(format t (cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst)))))))
但结果不正确
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我说好的,让我们创建一个局部变量,将字符串放在那里并打印它,但它再次不起作用
(defun show-dots (lst)
(let ((str (cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(show-dots (car lst))
(show-dots (cdr lst)))))))
(format t str)))
结果不正确
CS325-USER> (SHOW-DOTS '(A B C))
ABCNIL(NIL . NIL)(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A (B C)))
ABCNIL(NIL . NIL)(NIL . NIL)NIL(NIL . NIL)(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS '(A . B))
AB(NIL . NIL)
NIL
CS325-USER> (SHOW-DOTS NIL)
NIL
NIL
CS325-USER> (SHOW-DOTS '(NIL))
NILNIL(NIL . NIL)
NIL
所以我真的很想了解这里发生了什么,也许有些愚蠢,但我不明白这一点。
谢谢你的时间
如果返回字符串确实是问题说明的一部分,则使用 CONCATENATE 'STRING
。如果没有,甚至不用理会它,只需坚持 FORMAT T
并打印到控制台即可。
你生成字符串的原始函数实际上非常接近。问题在于,如果要递归调用生成字符串的函数,则不应打印该字符串,因为您不希望也打印中间字符串。您可以做的一个非常简单的更改是使 show-dots 函数的主体成为创建字符串的内部 helper 函数,并且然后在主函数中,打印辅助函数的结果:
(defun show-dots (lst)
(labels ((%show-dots (lst)
(cond
((atom lst) (format nil "~A" lst ))
((consp lst) (format nil "(~A . ~A)"
(%show-dots (car lst))
(%show-dots (cdr lst)))))))
(write-string (%show-dots lst))
nil))
CL-USER> (show-dots '(a b c))
(A . (B . (C . NIL)))
NIL
另一种方法是使用可选参数来指示字符串是应该打印还是 returned,它可以默认打印,但在递归情况下,你会 return 取而代之。实际上,由于 format 将 t 和 nil 作为具有这些语义的输出参数,您可以非常偷偷摸摸:
(defun show-dots (lst &optional (output t))
;; If OUTPUT is T (the default) then stuff will actually be printed,
;; and FORMAT returns NIL. If OUTPUT is NIL (as it is in the
;; recursive calls), then FORMAT creates the string and returns it,
(cond
((atom lst) (format output "~A" lst))
((consp lst) (format output "(~A . ~A)"
(show-dots (car lst) nil)
(show-dots (cdr lst) nil)))))
CL-USER> (show-dots '(a b c))
(A . (B . (C . NIL)))
NIL
也就是说,这两种实现最终都会创建一堆中间字符串,然后将它们连接在一起。这不是 space 的有效使用。在遍历正在打印的对象时写入流可能会更好。也许最直接的方法是自己处理括号和点的格式。这将导致或多或少像这样的解决方案(它 returns nil 因为那是你给出的第一个例子所做的):
(defun print-dotted (object &optional (stream *standard-output*))
"Print the object as usual, unless it is a cons, in which case
always print it in dotted notation. Return NIL."
(prog1 nil
(cond
;; write non-conses with WRITE
((not (consp object))
(write object :stream stream))
;; write the "(" " . " and ")" by hand,
;; and call print-dotted recursively for
;; the car and the cdr.
(t (write-char #\( stream)
(print-dotted (car object) stream)
(write-string " . " stream)
(print-dotted (cdr object) stream)
(write-char #\) stream)))))
CL-USER> (print-dotted '(a b c))
(A . (B . (C . NIL)))
;=> NIL
现在,format 函数实际上可以使用 tilde slash 指令调用以格式字符串命名的其他函数。这意味着你可以做这样的事情,我认为这有点优雅(我定义了一个新包,只是为了说明波浪线格式可以在其他包中查找符号;如果你在 CL-USER 中做事,你可以忽略它):
(defpackage ex
(:use "COMMON-LISP"))
(in-package #:ex)
(defun dot-cons (stream object &rest args)
(declare (ignore args))
(if (consp object)
(format stream "(~/ex:dot-cons/ . ~/ex:dot-cons/)" (car object) (cdr object))
(write object :stream stream)))
CL-USER> (format t "~/ex:dot-cons/" '(a b c))
(A . (B . (C . NIL)))
;=> NIL
试着理解它的作用:
CL-USER 9 > (format t "(~a ~a)" (princ 1) (princ 2))
12(1 2)
NIL
FORMAT
是一个函数。首先评估参数。 (princ 1)
得到评估。它打印 1
和 returns 1
。然后 (princ 2)
被评估。它打印 2
和 returns 2
。函数 FORMAT
被调用时使用评估的参数:t
、"(~a ~a)"
、1
和 2
。它打印 (1 2)
和 returns NIL
.
现在看这个:
CL-USER 8 > (progn (princ "(")
(princ 1)
(princ " . ")
(princ 2)
(princ ")"))
(1 . 2)
")"
打印 cons 单元格时重复使用上面的内容:
CL-USER 10 > (defun princme (c)
(if (consp c)
(progn
(princ "(")
(princme (car c))
(princ " . ")
(princme (cdr c))
(princ ")"))
(princ c)))
PRINCME
CL-USER 11 > (princme '(1 2 3))
(1 . (2 . (3 . NIL)))
注意:你原来的递归show-dots
生成字符串不是一个好主意。为什么?因为它递归地处理字符串并可能产生大量垃圾...
CL-USER 14 > (trace show-dots)
(SHOW-DOTS)
CL-USER 15 > (show-dots '((1 2) (3 (4 (5 6) ))))
0 SHOW-DOTS > ...
>> LST : ((1 2) (3 (4 (5 6))))
1 SHOW-DOTS > ...
>> LST : (1 2)
2 SHOW-DOTS > ...
>> LST : 1
2 SHOW-DOTS < ...
<< VALUE-0 : "1"
2 SHOW-DOTS > ...
>> LST : (2)
3 SHOW-DOTS > ...
>> LST : 2
3 SHOW-DOTS < ...
<< VALUE-0 : "2"
3 SHOW-DOTS > ...
>> LST : NIL
3 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
2 SHOW-DOTS < ...
<< VALUE-0 : "(2 . NIL)"
1 SHOW-DOTS < ...
<< VALUE-0 : "(1 . (2 . NIL))"
1 SHOW-DOTS > ...
>> LST : ((3 (4 (5 6))))
2 SHOW-DOTS > ...
>> LST : (3 (4 (5 6)))
3 SHOW-DOTS > ...
>> LST : 3
3 SHOW-DOTS < ...
<< VALUE-0 : "3"
3 SHOW-DOTS > ...
>> LST : ((4 (5 6)))
4 SHOW-DOTS > ...
>> LST : (4 (5 6))
5 SHOW-DOTS > ...
>> LST : 4
5 SHOW-DOTS < ...
<< VALUE-0 : "4"
5 SHOW-DOTS > ...
>> LST : ((5 6))
6 SHOW-DOTS > ...
>> LST : (5 6)
7 SHOW-DOTS > ...
>> LST : 5
7 SHOW-DOTS < ...
<< VALUE-0 : "5"
7 SHOW-DOTS > ...
>> LST : (6)
8 SHOW-DOTS > ...
>> LST : 6
8 SHOW-DOTS < ...
<< VALUE-0 : "6"
8 SHOW-DOTS > ...
>> LST : NIL
8 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
7 SHOW-DOTS < ...
<< VALUE-0 : "(6 . NIL)"
6 SHOW-DOTS < ...
<< VALUE-0 : "(5 . (6 . NIL))"
6 SHOW-DOTS > ...
>> LST : NIL
6 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
5 SHOW-DOTS < ...
<< VALUE-0 : "((5 . (6 . NIL)) . NIL)"
4 SHOW-DOTS < ...
<< VALUE-0 : "(4 . ((5 . (6 . NIL)) . NIL))"
4 SHOW-DOTS > ...
>> LST : NIL
4 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
3 SHOW-DOTS < ...
<< VALUE-0 : "((4 . ((5 . (6 . NIL)) . NIL)) . NIL)"
2 SHOW-DOTS < ...
<< VALUE-0 : "(3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL))"
2 SHOW-DOTS > ...
>> LST : NIL
2 SHOW-DOTS < ...
<< VALUE-0 : "NIL"
1 SHOW-DOTS < ...
<< VALUE-0 : "((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL)"
0 SHOW-DOTS < ...
<< VALUE-0 : "((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
"((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
所有对 FORMAT
的调用都在分配新字符串...
更好:
CL-USER 16 > (with-output-to-string (*standard-output*)
(princme '((1 2) (3 (4 (5 6) )))))
"((1 . (2 . NIL)) . ((3 . ((4 . ((5 . (6 . NIL)) . NIL)) . NIL)) . NIL))"
通常考虑流输出,而不是字符串连接。