Lisp函数以某种方式添加括号

时间:2014-03-12 22:04:03

标签: function recursion lisp

我正在尝试编写一个添加如下括号的函数:( parens'(a b c d e))返回(a(b(c(d(e)))))。我只是没有很好地看到这种模式。到目前为止,我只返回一个带有括号的列表。我似乎无法弄清楚如何让它看起来像那样。

(DEFUN PARENS (L)
   (COND ((NULL L) NIL)
   (T (CONS (LIST (CAR L)) (PARENS (CDR L))))))

2 个答案:

答案 0 :(得分:3)

列表中没有括号。您从一个包含五个元素(a b c d e)的列表开始,然后返回两个元素(a (b (c (d (e)))))的列表。第一个元素是a,第二个元素是另一个列表(b (c (d (e))))

使用reduce 非常容易关闭

CL-USER> (reduce 'list '(a b c d e) :from-end t)
(A (B (C (D E))))

您可以将reduce视为"注入"将函数list导入(a b c d e)以生成

(list a (list b (list c (list d e))))

这几乎是你想要的。你真的想要:

(list a (list b (list c (list d (list e)))))

你会如何制作?您可以递减列表,并为每个要返回(x . ys)的子列表(list x (recurse ys))递送,但ys()的情况除外。你不想进入(),因为你不想要一个包含两个元素的列表,你实际上什么都不想要。所以诀窍是比通常使用列表更早地停止递归。因此:

(defun parens (l)
  (cond
    ((endp l) '())
    ((endp (rest l)) l)
    ((list (first l) (parens (rest l))))))      ; *
CL-USER> (parens '(a b c d e))
(A (B (C (D (E)))))
CL-USER> (parens '(a b))
(A (B))
CL-USER> (parens '(a))
(A)
CL-USER> (parens '())
NIL

* 在最后一个句子中省略t测试是故意的。如果cond子句中没有正文表单,则返回测试的值。因此,(list …)既可以作为测试形式,也可以作为价值形式。

我们实际上可以清理一下。 ((endp l) '())的情况可能是((endp l) l),因为l是空列表。但这意味着在第一种和第二种情况下,我们都可以返回l。我们可以在Common Lisp中调用(rest '())并返回(),因此当(rest l)类似于() 时,l将为(e)l()。这意味着我们可以使用:

(defun parens (l)
  (cond
    ((endp (rest l)) l)
    ((list (first l) (parens (rest l))))))

如果我们只进行一次测试,我们也可以使用if

(defun parens (l)
  (if (endp (rest l))
      l
      (list (first l) (parens (rest l)))))

答案 1 :(得分:0)

您实际上可以使用reduce并对结尾进行一些特殊考虑:

(defun unflatten (list)
  (reduce #'list list
          :from-end t
          :end (1- (length list))
          :initial-value (last list)))

请注意,last会返回最后 n (默认为1)元素的列表。