Как удалить вложенные круглые скобки в LISP

Как я могу удалить вложенные круглые скобки рекурсивно в 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)

Спасибо

Ответы

Ответ 1

Вот что я сделал бы:

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

Это работает главным образом потому, что я уже Quicklisp уже установлен.

Ответ 2

(defun flatten (l)
  (cond ((null l) nil)
        ((atom l) (list l))
        (t (loop for a in l appending (flatten a)))))

Ответ 3

(defun flatten (l)
  (cond ((null l) nil)
        ((atom (car l)) (cons (car l) (flatten (cdr l))))
        (t (append (flatten (car l)) (flatten (cdr l))))))

Ответ 4

Я понимаю, что это старый поток, но это один из первых, который появляется, когда я google lisp сглаживается. Решение, которое я обнаружил, аналогично рассмотренному выше, но форматирование немного отличается. Я объясню это так, как будто вы новичок в 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, вы должны окружать ее скобками. Сначала это смутило меня.

Ответ 5

Вы можете определить его следующим образом:

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

Ответ 6

Lisp имеет функцию remove для удаления вещей. Здесь я использую версию REMOVE-IF, которая удаляет каждый элемент, для которого предикат является истинным. Я проверяю, является ли это скобкой и удаляет ее, если это правда.

Если вы хотите удалить круглые скобки, см. эту функцию:

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

Обратите внимание, однако, как упоминает Сванте, обычно нет "скопировать" круглые скобки.

Ответ 7

Это основанный на аккумуляторах подход. Локальная функция % flatten удерживает аккумулятор хвоста (правая часть списка, который уже был сплющен). Когда оставшаяся часть для выравнивания (левая часть списка) пуста, возвращается хвост. Когда часть, которая должна быть сплющена, является не-списком, она возвращает ту часть, которая префиксация на хвосте. Когда часть, подлежащая сглаживанию, является списком, она выравнивает остальную часть списка (с текущим хвостом), а затем использует этот результат в качестве хвоста для выравнивания первой части списка.

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

Ответ 8

Я знаю, что этот вопрос действительно старый, но я заметил, что никто не использовал push/nreverse idiom, поэтому я загружаю его здесь.

функция 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 и append. При этом двойной рекурсивный характер reverse-atomize действительно содержит собственные ОЗУ.

Ответ 9

(defun unnest (somewhat)
  (cond
   ((null somewhat) nil)
   ((atom somewhat) (list somewhat))
   (t
    (append (unnest (car somewhat)) (unnest (cdr somewhat))))))