我是Common Lisp的新手,我有问题。听起来是这样的:编写PRETTY-PRINT过程,该过程接受一个参数(一个广义列表),并使用以下规则进行打印:
(
...
)
列表中的任何元素都将使用相同的算法递归打印。
该函数应打印以下内容:
(pretty-print ' ( a (b c de) fg ))
( a
( b
c
de )
fg )
Run Code Online (Sandbox Code Playgroud)
我尝试自己重写函数几次,然后得到以下代码:
(defun print-list (elements)
(cond
((null elements) (princ ") "))
( t
(cond ((listp (car elements))
; (princ #\Space)
(princ "( ")
(print-list (car elements))
(format t "~%")
)
)
(if (atom (car elements))
(prin1 (car elements)))
(format t "~%")
(princ #\Space)
(print-list (cdr elements))
)
)
)
Run Code Online (Sandbox Code Playgroud)
但是它没有打印出应有的内容。有人可以帮我这个吗?我已经挣扎了一个星期。谢谢
第一步:使用常规格式。参见例如实用的普通Lisp。
(defun print-list (elements)
(cond
((null elements) (princ ") "))
(t
(cond ((listp (car elements))
;; (princ #\Space)
(princ "( ")
(print-list (car elements))
(format t "~%")))
(if (atom (car elements))
(prin1 (car elements)))
(format t "~%")
(princ #\Space)
(print-list (cdr elements)))))
Run Code Online (Sandbox Code Playgroud)
任务说要命名它pretty-print
,而任何标准函数都没有,所以让我们开始吧:
(defun pretty-print (elements)
(cond
((null elements) (princ ") "))
(t
(cond ((listp (car elements))
(princ "( ")
(pretty-print (car elements))
(format t "~%")))
(if (atom (car elements))
(prin1 (car elements)))
(format t "~%")
(princ #\Space)
(pretty-print (cdr elements)))))
Run Code Online (Sandbox Code Playgroud)
如果尝试(pretty-print '(a (b c de) fg))
,我们将得到:
A
( B
C
DE
)
FG
)
Run Code Online (Sandbox Code Playgroud)
至少所有符号都以正确的顺序排列,并且似乎只缺少一个括号。开头的括号似乎只在某些情况下才打印,但一开始就没有。
为了继续进行,我想稍微整理一下。在大多数情况下,不必嵌套cond
s和if
s;您可以将它们合并为一个cond
。为此,我们注意到(listp (car elements))
并且(atom (car elements))
似乎旨在相互排斥。由于null
检查应尽早退出,因此我们将通过显式返回以消除该嵌套。然后我们得到三种情况:
(defun pretty-print (elements)
(cond ((null elements)
(princ ") ")
(return-from pretty-print))
((listp (car elements))
(princ "( ")
(pretty-print (car elements))
(format t "~%"))
((atom (car elements))
(prin1 (car elements))))
(format t "~%")
(princ #\Space)
(pretty-print (cdr elements)))
Run Code Online (Sandbox Code Playgroud)
现在,对于开头的括号,我将尝试一个更简单的测试用例:空列表。
(pretty-print '())
Run Code Online (Sandbox Code Playgroud)
哪个打印
)
Run Code Online (Sandbox Code Playgroud)
那是不对的。但这正是第一cond
分支所说的。但是,如果我们在(princ "()")
此处进行操作,则在每个非空列表的末尾都会有一个附加的括号。
在这里,我们将两件事混为一谈:打印空列表,以及完成打印列表。如果只进行简单的递归,则无法区分这些内容,因为列表的每个尾部本身就是一个列表,最后一个为空。
我会在输入本身上做条件。现在您可以说类似“为了打印列表,先打印一个括号,然后再打印内容,然后是括号”。
(defun pretty-print (elements)
(cond ((null elements)
(princ "()")
(return-from pretty-print))
((listp elements)
(princ "(")
(pretty-print-elements elements)
(princ ")"))
((atom elements)
(prin1 elements)))
(format t "~%")
(princ #\Space))
Run Code Online (Sandbox Code Playgroud)
我将递归移动到列表中的另一个函数中(尚未显示)。现在,括号之间有了明确的匹配,并且不会出现一个被打印而另一个未被打印的情况。这是pretty-print-elements
递归样式(这样就不必引入更高级的运算符):
(defun pretty-print-elements (elements)
(unless (null elements)
(pretty-print (first elements))
(pretty-print-elements (rest elements))))
Run Code Online (Sandbox Code Playgroud)
让我们试试这些:
(pretty-print '())
()
(pretty-print '(a))
(A
)
(pretty-print '(a (b c de) fg))
(A
(B
C
DE
)
FG
)
Run Code Online (Sandbox Code Playgroud)
那还不是很漂亮,但是至少一切都匹配了。剩下两个问题:右括号不要放在新行上,并且元素应该对齐。
首先,我将元素之间的分隔符移到另一个函数中-实际上,这是打印列表的一部分,而不是打印元素。
(defun pretty-print (elements)
(cond ((null elements)
(princ "()")
(return-from pretty-print))
((listp elements)
(princ "(")
(pretty-print-elements elements)
(princ ")"))
((atom elements)
(prin1 elements))))
(defun pretty-print-elements (elements)
(unless (null elements)
(pretty-print (first elements))
(terpri)
(princ #\Space)
(pretty-print-elements (rest elements))))
Run Code Online (Sandbox Code Playgroud)
我也在terpri
这里介绍了它,它打印换行符。
现在很明显在最后一个元素之后省略该分隔:
(defun pretty-print-elements (elements)
(unless (null elements)
(pretty-print (first elements))
(unless (null (rest elements))
(terpri)
(princ #\Space))
(pretty-print-elements (rest elements))))
Run Code Online (Sandbox Code Playgroud)
同样,还有更简洁的方式来表达这一点,但让我们将其保持在基本语言水平上。试试吧:
(pretty-print '(a (b c de) fg))
(A
(B
C
DE)
FG)
Run Code Online (Sandbox Code Playgroud)
最后是缩进。当前,所有内容都缩进一个空格。但是,我们希望在递归中更深入地缩进,因此我们需要跟踪级别。
(defun pretty-print (elements indentation)
(cond ((null elements)
(princ "()")
(return-from pretty-print))
((listp elements)
(princ "(")
(pretty-print-elements elements (1+ indentation))
(princ ")"))
((atom elements)
(prin1 elements))))
(defun pretty-print-elements (elements indentation)
(unless (null elements)
(pretty-print (first elements) indentation)
(unless (null (rest elements))
(terpri)
(print-indentation indentation))
(pretty-print-elements (rest elements) indentation)))
Run Code Online (Sandbox Code Playgroud)
试试吧:
(pretty-print '(a (b c de) fg) 0)
(A
(B
C
DE)
FG)
Run Code Online (Sandbox Code Playgroud)
好的。这是格式化S表达式的方式。您的问题中显示的多余空格很可能是某些打印输出的误解。
的实现print-indentation
以及使其可选地指定初始缩进作为练习留给读者。您还可以通过查看实际上是多余的内容来使每个功能更加简洁。
归档时间: |
|
查看次数: |
61 次 |
最近记录: |