【问题标题】:How to remove nested parentheses in LISP如何删除 LISP 中的嵌套括号
【发布时间】:2011-02-10 11:20:14
【问题描述】:

如何在 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)

谢谢

【问题讨论】:

  • 您不删除括号。括号只是列表打印表示的一个方面。你正在做的是扁平化列表。

标签: lisp common-lisp parentheses


【解决方案1】:

在我访问这个问题时将其留在这里,只需要展平一个级别,后来自己发现(apply 'concatenate 'list ((1 2) (3 4) (5 6 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))))))
    

    【讨论】:

      【解决方案3】:

      大部分答案已经提到了Flatten问题的递归解决方案。使用 Common Lisp 对象系统的多重分派,您可以通过为 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)
      

      【讨论】:

        【解决方案4】:

        我知道这个问题真的很老,但我注意到没有人使用 push/nreverse 成语,所以我在这里上传。

        函数reverse-atomize 取出每个“原子”并将其放入下一次调用的output。最后,它会生成一个向后的扁平化列表,通过 atomize 函数中的 nreverse 函数解决。

        (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 '((a b) (c) d) 调用reverse-atomize,则会发生这种情况:

        (D C B A)
        

        人们喜欢使用 pushnreversenconc 等函数,因为它们使用的 RAM 少于各自的 consreverseappend 函数。话虽如此,reverse-atomize 的双重递归特性确实伴随着它自己的 RAMifications。

        【讨论】:

          【解决方案5】:

          这是一种基于累加器的方法。本地函数 %flatten 保留尾部的累加器(列表的 right 部分已被展平)。当剩余要展平的部分(列表的 left 部分)为空时,它返回尾部。当要展平的部分是非列表时,它会返回以尾部为前缀的部分。当要展平的部分是一个列表时,它会展平列表的 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)
          

          【讨论】:

            【解决方案6】:

            我意识到这是一个旧线程,但它是我在 google lisp flatten 时出现的第一个线程。我发现的解决方案与上面讨论的类似,但格式略有不同。我会像你刚接触 lisp 一样解释它,就像我第一次用谷歌搜索这个问题时一样,所以其他人很可能也是。

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

            append 函数会将第一个列表追加到第二个列表的开头。 另请注意,每次在 lisp 中使用函数时,都必须用括号括起来。起初这让我很困惑。

            【讨论】:

            • 它是尾递归的吗?
            • 它不是尾递归,而且问题并不重要:CL 规范没有要求消除尾调用,因此人们可能不依赖它。对于长列表和深度列表,这将爆炸。使用循环的另一个答案的解决方案只会炸毁深层列表。
            【解决方案7】:
            (defun unnest (somewhat)
              (cond
               ((null somewhat) nil)
               ((atom somewhat) (list somewhat))
               (t
                (append (unnest (car somewhat)) (unnest (cdr somewhat))))))
            

            【讨论】:

              【解决方案8】:

              Lisp 有 remove 删除东西的功能。在这里,我使用了一个版本REMOVE-IF,它删除了谓词为真的每个项目。我测试这个东西是否是一个括号,如果为真则删除它。

              如果要删除括号,请看这个函数:

              (defun unnest (thing)
                (read-from-string
                 (concatenate
                  'string
                  "("
                  (remove-if (lambda (c)
                               (member c '(#\( #\))))
                             (princ-to-string thing))
                  ")")))
              

              但请注意,正如 Svante 所提到的,通常不会“删除”括号。

              【讨论】:

                【解决方案9】:

                这是我要做的:

                (ql:quickload "alexandria")
                (alexandria:flatten list)
                

                这主要是因为我已经安装了Quicklisp

                【讨论】:

                • 如此谦虚!这主要是因为您已经创建了 Quicklisp。
                【解决方案10】:
                (defun flatten (l)
                  (cond ((null l) nil)
                        ((atom (car l)) (cons (car l) (flatten (cdr l))))
                        (t (append (flatten (car l)) (flatten (cdr l))))))
                

                【讨论】:

                  【解决方案11】:
                  (defun flatten (l)
                    (cond ((null l) nil)
                          ((atom l) (list l))
                          (t (loop for a in l appending (flatten a)))))
                  

                  【讨论】:

                  • 因为您附加的所有列表都来自第 3 行中的 list 调用,所以您可以使用 nconcing 而不是 appending
                  【解决方案12】:

                  你可以像这样定义它,例如:

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

                  【讨论】:

                    猜你喜欢
                    • 2019-08-11
                    • 1970-01-01
                    • 2022-10-19
                    • 2020-02-14
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2023-02-06
                    • 2017-06-03
                    相关资源
                    最近更新 更多