我正在尝试编写一个添加如下括号的函数:( 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))))))
答案 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)元素的列表。