Clojure でテキストファイルを読んでみる。

ファイルI/Oが少しなりともわかると応用が効きそうなので試してみました。まずは with-open を使ったものです。

;-- ファイルを行リストにする
(use '[clojure.contrib.duck-streams :only (reader)])
(defn file-to-lines [target]
  (with-open [r (reader target)]
    (loop [buf () line (.readLine r)]
      (if line
        (recur (cons line buf) (.readLine r))
        (reverse buf)))))

;-- 行番号をつける
(map vector (iterate inc 0) (file-to-lines "readme.txt"))

;-- "this" を含む要素だけを取り出す
(filter
  #(not= -1 (.indexOf (% 1) "this"))
  (map vector (iterate inc 0) (file-to-lines "readme.txt")))

;-- "this" を含まない要素だけを取り出す
(filter
  #(= -1 (.indexOf (% 1) "this"))
  (map vector (iterate inc 0) (file-to-lines "readme.txt")))

次に遅延評価でやってみます。途中で失敗したときに閉じてやらなければならないので try - catch を入れています。最後まで読まずに放置するとファイルが開きっぱなしになるので注意が必要です。

(use '[clojure.contrib.duck-streams :only (reader)])
(defn file-to-lines [target]
  (let [r (reader target)]
    (letfn [(readline []
      (try
        (let [line (.readLine r)]
          (if line
            (lazy-seq (cons line (readline)))
            (.close r)))
        (catch java.io.IOException _ (try (.close r) (catch Exception _ nil)))))]
      (readline))))

こういうのを途中で読み終える場合はどうしたらいいんでしょうか。さらに一段ラップして close 用のメソッドを提供するような作りにすればいいのかな。