Clojure を勉強し始めて2日目。
関数の引数にそのまま正規表現を渡そうとしてもうまく行かなかった。
例えば glob を自作しようと思い立って、
;; 正規表現とディレクトリを引数にして ;; マッチしたファイル名だけほしい (glob #"\.jpg" ".")
みたいなことがしたいとする。
補助関数を先に作ろう。
下記の ls-files-in-dir は与えられた dir 内のファイルを全てリストで返す。
(import '(java.io File)) (defn ls-files-in-dir [dir] (map #(.getName %) (.listFiles (File. dir))))
さて、glob を作るわけだが、下記の例はうまく動かない。
;; 動かない (defn bad-glob [re dir] (filter #(re-find re %) (ls-files-in-dir dir)))
なぜかといえば、関数の :tag メタデータに java.util.regex.Pattern を追加していないかららしい。
これから作ろうとしている glob と同じように、正規表現を引数に取る re-find のソースを確認して気付いた。
(use 'clojure.repl) (source re-find) ;; re-find のソースが表示される
上記を踏まえて glob を定義してみよう。
(defn glob [^java.util.regex.Pattern re dir] (filter #(re-find re %) (ls-files-in-dir dir)))
これで動く。
もう一工夫。
glob が正規表現を引数として受け取らなかった場合は dir 内のファイルをそのまま表示するようにしてみよう。
全体をまとめて書く。
(import '(java.io File)) (defn ls-files-in-dir [dir] (map #(.getName %) (.listFiles (File. dir)))) (defn glob ;; dir だけ受け取ったとき ([dir] (ls-files-in-dir dir)) ;; 正規表現と dir の2つを受け取ったとき ([^java.util.regex.Pattern re dir] (filter #(re-find re %) (ls-files-in-dir dir)))) ;; ----------------- ;; さて使ってみよう (glob "photo") ->("bw_2014.tiff" "IMAG0001.jpg" "IMG_2836.jpg") (glob #"\.jpg" "photo") ->("IMAG0001.jpg" "IMG_2836.jpg")