2009-09-10 6 views
22

estoy usando OpenMCL sobre Darwin, y me gustaría hacer algo como:¿Cómo puedo iterar a través de un directorio en Common Lisp?

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

Pero no puedo conseguir directory para volver otra cosa que no sea NIL, y me parece que no puede encontrar ninguna buena explicación en línea (aparte de "es diferente para cada sistema").

¿Alguna sugerencia?

Respuesta

16

¿Su especificación de ruta contiene un comodín? cosas ruta de Common Lisp es un poco difícil de entender al principio - al menos para mí fue ... Como los CLHS estados de la función directory:

Si el pathspec no es salvaje, la lista resultante contendrá ya sea cero o uno elementos.

Con el fin de tener su nombre de ruta incluye un comodín, puede probar la función de maquillaje nombre de ruta, como

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

O incluso

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

encontré la biblioteca CL-FAD una gran ayuda para tratar con nombres de rutas y el sistema de archivos. En particular, su función list-directory puede ser más fácil de usar que la función estándar directory.

+2

Sí funcionó para mí - (directorio "ruta") devuelve NIL, donde (directorio "nombre de ruta /*.*") me dio los resultados esperados. – Justicle

+1

¿Solo necesita archivos con nombres que contengan un punto? – Svante

+0

Weird eh? De hecho, estoy buscando archivos .h y .cpp, pero "pathname/*" devuelve NIL. – Justicle

26

Hay básicamente dos formas de especificar los nombres de ruta:

  • utilizando cuerdas

Las cadenas son, obviamente, dependiendo de la plataforma: la sintaxis Unix frente a la sintaxis de Windows, por ejemplo.

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

Usted puede crear un objeto de ruta de una cadena:

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

El #p anterior asegura que se crea un objeto de ruta (y no una cadena), cuando se lee de nuevo.

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

Así, Lisp común internamente trabaja con objetos de nombre de ruta, pero se le permite utilizar cadenas normales y hace que los objetos de nombre de ruta de ellos si es necesario.

Cuando Common Lisp ve un nombre de ruta que no tiene todos los componentes especificados (por ejemplo, falta el directorio), rellena los componentes del objeto pathname que es el valor de variabel * DEFAULT-PATHNAME-DEFAULTS *.

Con la función describe se puede ver en los componentes de un nombre de ruta (aquí 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 
  • utilizando la ruta funciones Lisp crear objetos

MAKE-PATHNAME es la función y se necesitan algunos argumentos clave para especificar los componentes.

A veces también es útil para crear un nuevo nombre de ruta basada en una ya existente:

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

Si utiliza ANUARIO es útil el uso de un nombre de ruta con comodines. DIRECTORIO luego devolverá una lista de nombres de ruta coincidentes. El nombre 'DIRECTORIO' es ligeramente engañoso, ya que DIRECTORIO no enumera el contenido de un directorio, sino que enumera los nombres de las rutas correspondientes para (normalmente) una ruta de acceso con comodines. Los comodines pueden coincidir con una secuencia de caracteres en componentes como /foo/s*c/list*.l* ". También está el comodín **, que se usa para hacer coincidir partes de una jerarquía de directorios como/foo/** /test.lisp, que coincide con todos los archivos test.lisp bajo el directorio foo y sus subdirectorios.

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

anterior debe devolver una lista de todos los archivos Lisp '' en '/ Usuarios/foo/Lisp /' y todos sus subdirectorios

Para devolver los archivos .c en un solo directorio de uso:.

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

nota que DIRE CTORY devuelve una lista de objetos de ruta (no una lista de cadenas).

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

Above utiliza un objeto de nombre de ruta creado por MAKE-PATHNAME. Devuelve todos los archivos que coinciden con /Lisp/cl-http/cl-http-342/server/md5.*.

Esto es lo mismo que:

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

que es más corto, pero depende de la sintaxis nombre de ruta Unix.

+0

+1 Un buen resumen de nombres de ruta en LISP, muy útil. – Justicle

8

La biblioteca de Common Lisp moderna que implementa el listado de directorios es IOLIB.

funciona así:

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

Tenga en cuenta que no se requiere ninguna barra inclinada o comodines. Es muy robusto e incluso puede procesar nombres de archivos con caracteres Unicode codificados incorrectamente.

diferencias en comparación con CL-FAD:

  • Los objetos que dan son las rutas de archivos IOLIB, un reemplazo para los nombres de ruta de CL, que está más cerca de lo que hace el sistema operativo subyacente.
  • IOLIB implementa sus rutinas usando CFFI, por lo que funciona igual en todas las implementaciones Lisp (siempre que IOLIB tenga un back-end para el sistema operativo), en contraste con CL-FAD, que intenta abstraer la función DIRECTORY de la implementación con todo su peculiaridades
  • En contraste con CL-FAD, iolib maneja correctamente los enlaces simbólicos (un problema importante con CL-FAD que lo hace virtualmente inutilizable en plataformas que no sean Windows IMHO).
1

Voy a agregar un ejemplo que me funciona, por el bien de un fragmento de código. Yo uso osicat (similar a cl-fad) y str.

editar: también con uiop:directory-files. str: contiene? podría hacerse con search.

;; searching for "ref". 
(setf *data-directory* "~/books/lisp") 
(remove-if-not (lambda (it) 
        (str:contains? "ref" (namestring it))) 
       (osicat:list-directory *data-directory*)) 

vuelve

(#P"~/books/lisp/common-lisp-quick-reference-clqr-a4-booklet-all.pdf" 
#P"~/books/lisp/common-lisp-quick-reference-clqr-a4-consec.pdf" 
#P"~/books/lisp/commonLisp-interactive-approach-reference-buffalo.pdf") 

Ciertamente se pueden mejorar mi un uso adecuado de los comodines. Sin embargo eso es un fragmento de código se puede utilizar ahora:)

Referencias: