В вашем коде неправильно то, что вы используете EQ. EQ сравнивает для идентичности.
EQ не для сравнения чисел. Эквалайзер двух чисел может быть истинным или ложным.
Используйте EQL, если вы хотите сравнивать по тождествам, числам по значению или символам. Не эквалайзер.
На самом деле
(remove-if (lambda (x) (eql x item)) list)
это просто
(remove item list)
Для вашего кода ошибка эквалайзера COULD означает, что перестановка вызывается в рекурсивном вызове без фактического удаления номера из списка.
Кроме этого, я думаю, что SBCL просто занят управлением памятью. SBCL на моем Mac приобрела много памяти (больше ГБ) и была занята чем-то. Через некоторое время результат был вычислен.
Ваша рекурсивная функция генерирует огромное количество мусора. LispWorks говорит: 1360950192 байт
Может быть, вы можете придумать более эффективную реализацию?
Обновление: мусор
Lisp обеспечивает некоторое автоматическое управление памятью, но это не освобождает программиста от мысли о космических эффектах.
Lisp использует как стек, так и кучу для выделения памяти. Куча может быть структурирована определенным образом для GC - например, в поколениях, полупространствах и / или областях. Существуют точные сборщики мусора и «консервативные» сборщики мусора (используются SBCL на машинах Intel).
При запуске программы мы видим различные эффекты:
обычные рекурсивные процедуры выделяют место в стеке. Проблема: размер стека обычно фиксирован (хотя некоторые Лиспы могут увеличить его в обработчике ошибок).
программа может выделять огромный объем памяти и возвращать большой результат. PERMUTE - это такая функция. Может возвращать очень большие списки.
программа может выделять память и использовать ее временно, а затем сборщик мусора может перерабатывать ее. Скорость создания и уничтожения может быть очень высокой, даже если программа не использует большой объем фиксированной памяти.
Хотя есть и другие проблемы. Но для каждого из вышеперечисленного программист на Лиспе (и любой другой программист, использующий языковую реализацию со сборкой мусора) должен научиться справляться с этим.
Заменить рекурсию итерацией. Заменить рекурсию хвостовой рекурсией.
Возвращать только ту часть результата, которая необходима, и не генерировать полное решение. Если вам нужна n-я перестановка, вычислите ее, а не все перестановки. Используйте ленивые структуры данных, которые вычисляются по требованию. Используйте что-то вроде SERIES, что позволяет использовать потоковые, но эффективные вычисления. См. SICP, PAIP и другие продвинутые книги по Лиспу.
Повторное использование памяти с менеджером ресурсов. Повторно используйте буферы вместо того, чтобы распределять объекты постоянно. Используйте эффективный сборщик мусора со специальной поддержкой для сбора эфемерных (недолговечных) объектов. Иногда это также может помочь деструктивно модифицировать объекты вместо выделения новых объектов.
Выше рассматриваются космические проблемы реальных программ. В идеале наши компиляторы или инфраструктура времени выполнения могут предоставлять некоторую автоматическую поддержку для решения этих проблем. Но на самом деле это не очень работает. Большинство систем Lisp предоставляют низкоуровневую функциональность для решения этой проблемы, а Lisp предоставляет изменяемые объекты - потому что опыт реальных программ на Lisp показал, что программисты хотят использовать их для оптимизации своих программ. Если у вас есть большое CAD-приложение, которое вычисляет форму турбинных лопаток, то теоретические / пуристические представления о неизменяемой памяти просто не применимы - разработчику нужен более быстрый / меньший код и меньший объем времени выполнения.