问题1编写PRETTY-PRINT过程,该过程接受一个参数(广义列表),并使用以下规则进行打印

1 common-lisp

我是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)

但是它没有打印出应有的内容。有人可以帮我这个吗?我已经挣扎了一个星期。谢谢

Sva*_*nte 5

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

至少所有符号都以正确的顺序排列,并且似乎只缺少一个括号。开头的括号似乎只在某些情况下才打印,但一开始就没有。

为了继续进行,我想稍微整理一下。在大多数情况下,不必嵌套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)))
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以及使其可选地指定初始缩进作为练习留给读者。您还可以通过查看实际上是多余的内容来使每个功能更加简洁。