Подтвердить что ты не робот

Как выполнить итерацию через каталог в Common Lisp?

Я использую OpenMCL для Дарвина, и я хотел бы сделать что-то вроде:

(loop for f in (directory "somedir")
  collect (some-per-file-processing f))

Но я не могу получить directory, чтобы вернуть что-либо, кроме NIL, и я не могу найти никаких полезных объяснений в Интернете (кроме "для разных систем" ).

Любые указатели?

4b9b3361

Ответ 1

Указывает ли ваша спецификация имени пути подстановочный знак? Общий материал Lisp pathname довольно сложно понять поначалу - по крайней мере, для меня это было... Поскольку CLHS указывает на directory:

Если pathspec не является диким, итоговый список будет содержать либо ноль или один элемент.

Чтобы ваш путь включал подстановочный знак, вы можете попробовать функцию make-pathname, например

(directory (make-pathname :directory '(:absolute "srv" "hunchentoot") :name :wild :type "lisp"))

Или даже

(directory (make-pathname :directory '(:absolute "srv" "hunchentoot") :name :wild :type :wild))

Я нашел библиотеку CL-FAD большую помощь для работы с именами путей и файловой системой. В частности, его функция list-directory может быть проще в использовании, чем обычная стандартная функция directory.

Ответ 2

Существует два способа указать пути:

  • с использованием строк

Строки, очевидно, зависят от платформы: синтаксис Unix или синтаксис Windows, например.

"/Users/foo/bar.text"  is a valid pathname
"/Users/foo/*/foo.*"   is a valid pathname with two wildcards

Вы можете создать объект pathname из строки:

? (pathname "/Users/bar/foo.text")
#P"/Users/bar/foo.text"

#p above гарантирует, что объект pathname (а не строка) создается, когда вы его читаете.

? #P"/Users/bar/foo.text"
#P"/Users/bar/foo.text"

Итак, внутренне Common Lisp работает с объектами pathname, но он позволяет вам использовать обычные строки и при необходимости создавать объекты пути из них.

Когда Common Lisp видит имя пути, у которого не все компоненты указаны (например, каталог отсутствует), тогда он заполняет компоненты из объекта pathname, который является значением переменной variabel * DEFAULT-PATHNAME-DEFAULTS *.

С помощью функции DESCRIBE вы можете посмотреть компоненты пути (здесь Clozure CL):

? (describe (pathname "/Users/bar/*.text"))
#P"/Users/bar/*.text"
Type: PATHNAME
Class: #<BUILT-IN-CLASS PATHNAME>
TYPE: (PATHNAME . #<CCL::CLASS-WRAPPER PATHNAME #x3000401D03BD>)
%PATHNAME-DIRECTORY: (:ABSOLUTE "Users" "bar")
%PATHNAME-NAME: :WILD
%PATHNAME-TYPE: "text"
%PHYSICAL-PATHNAME-VERSION: :NEWEST
%PHYSICAL-PATHNAME-DEVICE: NIL
  • с помощью функций Lisp, создающих объекты с именем пути

MAKE-PATHNAME - это функция, и для указания компонентов требуется несколько аргументов ключевых слов.

Иногда также полезно создать новый путь на основе существующего:

(make-pathname :name "foo" :defaults (pathname "/Users/bar/baz.text"))

Если вы используете DIRECTORY, полезно использовать имя пути с подстановочными знаками. DIRECTORY затем вернет список подходящих путей. Имя "DIRECTORY" немного вводит в заблуждение, поскольку DIRECTORY не перечисляет содержимое каталога, но перечисляет соответствующие пути для (обычно) пути с помощью подстановочных знаков. Подстановочные знаки могут соответствовать последовательностям символов в таких компонентах, как /foo/s *c/list*.l* ". Существует также дикая карта **, которая используется для сопоставления частей иерархии каталогов, например /foo/ **/test.lisp, который соответствует всем файлам test.lisp в каталоге foo и его подкаталогах.

(directory "/Users/foo/Lisp/**/*.lisp")

Выше должен возвращать список всех файлов lisp 'в'/Users/foo/Lisp/'и всех его подкаталогах.

Чтобы вернуть файлы .c в одном каталоге, используйте:

(directory "/Users/foo/c/src/*.c")

Обратите внимание, что DIRECTORY возвращает список объектов пути (не список строк).

? (directory (make-pathname
               :name "md5"
               :type :wild
               :directory '(:absolute "Lisp" "cl-http" "cl-http-342" "server")))
(#P"/Lisp/cl-http/cl-http-342/server/md5.lisp"
 #P"/Lisp/cl-http/cl-http-342/server/md5.xfasl")

Выше используется объект pathname, созданный MAKE-PATHNAME. Он возвращает все файлы, которые соответствуют / Lisp/cl-http/cl-http-342/server/md5.*.

Это то же самое, что:

(directory "/Lisp/cl-http/cl-http-342/server/md5.*")

который короче, но зависит от синтаксиса пути Unix.

Ответ 3

Современная общая библиотека Lisp, реализующая список каталогов, IOLIB.

Он работает следующим образом:

CL-USER> (iolib.os:list-directory "/etc/apt")
(#/p/"trusted.gpg~" #/p/"secring.gpg" #/p/"trustdb.gpg" #/p/"sources.list"
 #/p/"sources.list~" #/p/"apt-file.conf" #/p/"apt.conf.d" #/p/"trusted.gpg"
 #/p/"sources.list.d")

Обратите внимание, что никаких обязательных косой черты или подстановочных знаков не требуется. Он очень надежный и даже может обрабатывать имена файлов с некорректно кодированными символами юникода.

Различия по сравнению с CL-FAD:

  • Объекты, которые вы получаете, - это пути к файлам IOLIB, замена имен путей CL, которые ближе к основной ОС.
  • IOLIB реализует свои подпрограммы с использованием CFFI, поэтому он работает одинаково во всех реализациях Lisp (при условии, что IOLIB имеет бэкэнд для операционной системы), в отличие от CL-FAD, который пытается абстрагироваться от реализации функции DIRECTORY с помощью все его причуды.
  • В отличие от CL-FAD, iolib правильно работает с символическими ссылками (одна из основных проблем с CL-FAD, которая делает его практически непригодным для использования на других платформах, кроме Windows IMHO).