如何在Common LISP中递归删除嵌套括号,例如
(unnest '(a b c (d e) ((f) g))) => (a b c d e f g)
(unnest '(a b)) => (a b)
(unnest '(() ((((a)))) ())) => (a)
由于
答案 0 :(得分:17)
答案 1 :(得分:14)
(defun flatten (l)
(cond ((null l) nil)
((atom l) (list l))
(t (loop for a in l appending (flatten a)))))
答案 2 :(得分:6)
(defun flatten (l)
(cond ((null l) nil)
((atom (car l)) (cons (car l) (flatten (cdr l))))
(t (append (flatten (car l)) (flatten (cdr l))))))
答案 3 :(得分:6)
我意识到这是一个老话题,但它是我google lisp flatten时出现的第一个。我发现的解决方案与上面讨论的类似,但格式略有不同。我会解释它好像你是lisp的新手一样,就像我第一次用Google搜索这个问题一样,所以很可能其他人也会这样。
(defun flatten (L)
"Converts a list to single level."
(if (null L)
nil
(if (atom (first L))
(cons (first L) (flatten (rest L)))
(append (flatten (first L)) (flatten (rest L))))))
对于那些刚接触lisp的人来说,这是一个简短的总结。
以下行声明了一个名为flatten的函数,其参数为L。
(defun flatten (L)
以下行检查空列表。
(if (null L)
下一行返回nil,因为cons ATOM nil声明一个包含一个条目(ATOM)的列表。这是递归的基本情况,让函数知道何时停止。此后的行检查列表中的第一项是否是原子而不是另一个列表。
(if (atom (first L))
然后,如果是,它使用递归来创建这个原子的扁平列表,并与函数将生成的其余扁平列表相结合。 cons将原子与另一个列表结合起来。
(cons (first L) (flatten (rest L)))
如果它不是一个原子,那么我们必须对它进行压扁,因为它是另一个可能在其中有更多列表的列表。
(append (flatten (first L)) (flatten (rest L))))))
追加功能会将第一个列表追加到第二个列表的开头。 另请注意,每次在lisp中使用函数时,都必须用括号括起来。起初这让我很困惑。
答案 4 :(得分:3)
您可以像这样定义它:
(defun unnest (x)
(labels ((rec (x acc)
(cond ((null x) acc)
((atom x) (cons x acc))
(t (rec (car x) (rec (cdr x) acc))))))
(rec x nil)))
答案 5 :(得分:3)
大多数答案已经提到了 Flatten 问题的递归解决方案。使用Common Lisp Object System的多重分派,可以通过为3种可能的情况定义3种方法来递归地解决该问题:
(defmethod flatten ((tree null))
"Tree is empty list."
())
(defmethod flatten ((tree list))
"Tree is a list."
(append (flatten (car tree))
(flatten (cdr tree))))
(defmethod flatten (tree)
"Tree is something else (atom?)."
(list tree))
(flatten '(2 ((8) 2 (9 (d (s (((((a))))))))))) ; => (2 8 2 9 D S A)
答案 6 :(得分:3)
在我访问此问题时,只需将其平整,然后自己弄清楚(apply 'concatenate 'list ((1 2) (3 4) (5 6 7)))
在这种情况下是一种更干净的解决方案。
答案 7 :(得分:2)
这个受欢迎的问题仅具有递归解决方案(不计算Rainer的答案)。
我们有一个循环版本:
(defun flatten (tree &aux todo flat)
(check-type tree list)
(loop
(shiftf todo tree nil)
(unless todo (return flat))
(dolist (elt todo)
(if (listp elt)
(dolist (e elt)
(push e tree))
(push elt flat))))))
答案 8 :(得分:1)
Lisp具有删除内容的函数remove
。在这里,我使用版本REMOVE-IF
删除谓词为true的每个项目。我测试该东西是否为括号,如果为真,则将其删除。
如果要删除括号,请参阅此函数:
(defun unnest (thing)
(read-from-string
(concatenate
'string
"("
(remove-if (lambda (c)
(member c '(#\( #\))))
(princ-to-string thing))
")")))
但请注意,正如Svante所提到的,人们通常不会“删除”括号。
答案 9 :(得分:1)
这是一种基于累加器的方法。本地函数%flatten 保留尾部的累加器(列表的右部分已被展平)。当剩余要平展的部分(列表的左部分)为空时,它返回尾部。当要展平的零件是非列表时,它会将该零件前缀返回到尾部。当要展平的零件是一个列表时,它会使列表的 rest 变平(使用当前尾部),然后使用该结果作为尾部来展平列表的第一部分。
(defun flatten (list)
(labels ((%flatten (list tail)
(cond
((null list) tail)
((atom list) (list* list tail))
(t (%flatten (first list)
(%flatten (rest list)
tail))))))
(%flatten list '())))
CL-USER> (flatten '((1 2) (3 4) ((5) 6) 7))
(1 2 3 4 5 6 7)
答案 10 :(得分:1)
我知道这个问题真的很老但是我注意到没有人使用过push / nreverse习语,所以我在这里上传。
函数reverse-atomize
取出每个“原子”并将其放入下一个调用的output
。最后,它生成一个向后的扁平列表,使用nreverse
函数中的atomize
函数解析。
(defun reverse-atomize (tree output)
"Auxillary function for atomize"
(if (null tree)
output
(if (atom (car tree))
(reverse-atomize (cdr tree) (push (car tree) output))
(reverse-atomize (cdr tree) (nconc (reverse-atomize (car tree)
nil)
output)))))
(defun atomize (tree)
"Flattens a list into only the atoms in it"
(nreverse (reverse-atomize tree nil)))
所以调用atomize '((a b) (c) d)
看起来像这样:
(A B C D)
如果您使用reverse-atomize
致电reverse-atomize '((a b) (c) d)
,则会发生这种情况:
(D C B A)
人们喜欢使用push
,nreverse
和nconc
等功能,因为他们使用的内存少于各自的cons
,reverse
和{{1} }} 功能。据说append
的双重递归性质确实伴随着它自己的 RAM 。
答案 11 :(得分:0)
(defun unnest (somewhat)
(cond
((null somewhat) nil)
((atom somewhat) (list somewhat))
(t
(append (unnest (car somewhat)) (unnest (cdr somewhat))))))