问题 1 编写 PRETTY-PRINT 过程,该过程采用一个参数(通用列表),并使用以下规则打印它



我是Common Lisp的新手,我有一个问题。 听起来是这样的: 编写 PRETTY-PRINT 过程,该过程采用一个参数(通用列表),并使用以下规则打印它:

(

)

任何元素(即列表)都将使用相同的算法递归打印。

该函数应打印以下内容:

(pretty-print ' ( a (b c de) fg ))
( a 
( b 
c 
de ) 
fg )

我尝试自己重写函数几次,我得到了这段代码:

(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))
)
) 
)

但它没有打印它应该打印的东西。谁能帮我解决这个问题?我已经挣扎了一个星期。谢谢

第一步:使用常规格式。 参见例如 Practical Common 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)))))

任务说要命名为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)))))

如果我们尝试(pretty-print '(a (b c de) fg)),我们会得到:

A
( B
C
DE
) 
FG
) 

至少所有的符号都以正确的顺序存在,似乎只缺少一个括号。 左括号似乎只在某些情况下打印,但在开头就缺失了。

为了继续,我想清理一下。 在大多数情况下,嵌套conds 和ifs 是不必要的;您可以将它们合并为一个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)))

现在,对于左括号,我将尝试一个更简单的测试用例:空列表。

(pretty-print '())

哪些打印

)

这是不对的。 但这正是第一个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))

我将递归移动到列表中的其他函数中(尚未显示)。 现在括号有一个明确的匹配,不可能有一个被打印而另一个没有。 这里以递归风格pretty-print-elements(这样我们就不必介绍更高级的运算符):

(defun pretty-print-elements (elements)
(unless (null elements)
(pretty-print (first elements))
(pretty-print-elements (rest elements))))

让我们试试这些:

(pretty-print '())
()
(pretty-print '(a))
(A
)
(pretty-print '(a (b c de) fg))
(A
(B
C
DE
)
FG
)

这还不是很漂亮,但至少一切都匹配。 还剩下两个问题:右括号不应在新行上,元素应对齐。

首先,我将元素之间的分隔符移动到另一个函数中 — 它实际上是打印列表的一部分,而不是打印元素的一部分。

(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))))

我还在这里介绍了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))))

同样,有更简洁的方式来表达这一点,但让我们把它保持在基本的语言层面上。 试试吧:

(pretty-print '(a (b c de) fg))
(A
(B
C
DE)
FG)

最后是缩进。 目前,所有内容都缩进一个空格。 但是,我们希望在递归中越深入,就越缩进它,因此我们需要跟踪级别。

(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)))

试试吧:

(pretty-print '(a (b c de) fg) 0)
(A
(B
C
DE)
FG)

好。 这就是格式化 S 表达式的方法。 问题中显示的额外空格很可能是某些打印输出的误解。

print-indentation的实现以及指定初始缩进作为可选留给读者的练习。 您还可以通过查看实际冗余的内容来使这些函数中的每一个更加简洁。

最新更新