Ответ 1
Вот что я сделал бы:
(ql:quickload "alexandria")
(alexandria:flatten list)
Это работает главным образом потому, что я уже Quicklisp уже установлен.
Как я могу удалить вложенные круглые скобки рекурсивно в 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)
Спасибо
Вот что я сделал бы:
(ql:quickload "alexandria")
(alexandria:flatten list)
Это работает главным образом потому, что я уже Quicklisp уже установлен.
(defun flatten (l)
(cond ((null l) nil)
((atom l) (list l))
(t (loop for a in l appending (flatten a)))))
(defun flatten (l)
(cond ((null l) nil)
((atom (car l)) (cons (car l) (flatten (cdr l))))
(t (append (flatten (car l)) (flatten (cdr l))))))
Я понимаю, что это старый поток, но это один из первых, который появляется, когда я 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, вы должны окружать ее скобками. Сначала это смутило меня.
Вы можете определить его следующим образом:
(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)))
Lisp имеет функцию remove
для удаления вещей. Здесь я использую версию REMOVE-IF
, которая удаляет каждый элемент, для которого предикат является истинным. Я проверяю, является ли это скобкой и удаляет ее, если это правда.
Если вы хотите удалить круглые скобки, см. эту функцию:
(defun unnest (thing)
(read-from-string
(concatenate
'string
"("
(remove-if (lambda (c)
(member c '(#\( #\))))
(princ-to-string thing))
")")))
Обратите внимание, однако, как упоминает Сванте, обычно нет "скопировать" круглые скобки.
Это основанный на аккумуляторах подход. Локальная функция % 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)
Я знаю, что этот вопрос действительно старый, но я заметил, что никто не использовал 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
действительно содержит собственные ОЗУ.
(defun unnest (somewhat)
(cond
((null somewhat) nil)
((atom somewhat) (list somewhat))
(t
(append (unnest (car somewhat)) (unnest (cdr somewhat))))))