Common Lispでディレクトリを反復処理するにはどうすればよいですか?
-
05-07-2019 - |
質問
ダーウィンでOpenMCLを使用していますが、次のようなことをしたいです:
(loop for f in (directory "somedir")
collect (some-per-file-processing f))
しかし、 NIL
以外の何かを返す directory
を取得することはできません。また、("システムごとに」)。
任意のポインター?
解決
パス名の指定にワイルドカードが含まれていますか? Common Lispのパス名は、最初は把握するのが少し難しい-少なくとも私にとっては... CLHS は、 directory
関数の状態を示します。
pathspecがワイルドでない場合、 結果のリストには次のいずれかが含まれます 0個または1個の要素。
パス名にワイルドカードを含めるには、次のように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 "/Users/bar/foo.text")
#P"/Users/bar/foo.text"
上記の#pは、読み返すときにパス名オブジェクト(文字列ではなく)が作成されることを保証します。
? #P"/Users/bar/foo.text"
#P"/Users/bar/foo.text"
つまり、内部的にCommon Lispはパス名オブジェクトを処理しますが、通常の文字列を使用し、必要に応じてそれらからパス名オブジェクトを作成できます。
Common Lispは、すべてのコンポーネントが指定されていないパス名(ディレクトリが欠落しているなど)を検出すると、variabel * DEFAULT-PATHNAME-DEFAULTS *の値であるパス名オブジェクトからコンポーネントを埋めます。 / p>
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 *&quot;などのコンポーネント内の文字シーケンスに一致できます。ワイルドカード**もあります。これは、ディレクトリfooとそのサブディレクトリの下にあるすべてのファイルtest.lispに一致する/foo/**/test.lispなどのディレクトリ階層の一部に一致するために使用されます。
(directory "/Users/foo/Lisp/**/*.lisp")
上記は、「/ Users / foo / Lisp /」およびそのすべてのサブディレクトリにあるすべての「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")
上記では、MAKE-PATHNAMEによって作成されたパス名オブジェクトを使用します。 /Lisp/cl-http/cl-http-342/server/md5.*に一致するすべてのファイルを返します。
これは次と同じです:
(directory "/Lisp/cl-http/cl-http-342/server/md5.*")
これは短いですが、Unixパス名の構文に依存します。
ディレクトリリストを実装する最新のCommon 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")
末尾のスラッシュやワイルドカードは不要です。非常に堅牢で、誤ってエンコードされたUnicode文字を含むファイル名も処理できます。
CL-FADとの違い:
- 取得するオブジェクトはIOLIBファイルパスであり、CLのパス名に代わるものであり、基盤となるOSの動作に近いものです。
- IOLIBはCFFIを使用してルーチンを実装するため、すべてのLisp実装で同じ動作をします(IOLIBにオペレーティングシステムのバックエンドがある場合)。CL-FADは、そのすべての癖。
- CL-FADとは異なり、iolibはシンボリックリンクを正しく処理します(Windows IMHO以外のプラットフォームでは事実上使用できなくなるCL-FADの1つの大きな問題)。
コードスニペットのために、私のために機能する例を追加します。 osicat を使用します(cl-fadと同様)および str 。
編集: uiop:directory-files
も使用します。 str:を含む? search
でできます。
;; searching for "ref".
(setf *data-directory* "~/books/lisp")
(remove-if-not (lambda (it)
(str:contains? "ref" (namestring it)))
(osicat:list-directory *data-directory*))
返品
(#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")
ワイルドカードの適切な使用により、確かに改善できます。ただし、これは今すぐ使用できるスニペットです:)
参照: