3

Clojureを使用して行ごとにYAMLフロントマターを含む(または含まない)ファイルを読み込もうとしています.2つのベクトルを持つハッシュマップを返します。 .

入力ファイルの例は次のようになります。

---
key1: value1
key2: value2
---

Body text paragraph 1

Body text paragraph 2

Body text paragraph 3

私はこれを行う機能するコードを持っていますが、私の(明らかにClojureの経験がない)鼻には、コードの匂いがします。

(defn process-file [f]
  (with-open [rdr (java.io.BufferedReader. (java.io.FileReader. f))]
    (loop [lines (line-seq rdr) in-fm 0 frontmatter [] body []]
      (if-not (empty? lines)
        (let [line (string/trim (first lines))]
          (cond
            (zero? (count line))
              (recur (rest lines) in-fm frontmatter body)
            (and (< in-fm 2) (= line "---")) 
              (recur (rest lines) (inc in-fm) frontmatter body)
            (= in-fm 1)  
              (recur (rest lines) in-fm (conj frontmatter line) body)
            :else          
             (recur (rest lines) in-fm frontmatter (conj body line))))
        (hash-map :frontmatter frontmatter :body body)))))

誰かがこれを行うためのよりエレガントな方法を教えてもらえますか? このプロジェクトでは、かなりの量の行ごとの解析を行う予定です。可能であれば、より慣用的な方法で処理したいと考えています。

4

2 に答える 2

6

まず、ファイルを実際に読み取る関数から呼び出されるように、行処理ロジックを独自の関数に入れます。さらに良いことに、IO を処理する関数に、おそらく次の行に沿って、行を引数としてマップする関数を持たせることができます。

(require '[clojure.java.io :as io])

(defn process-file-with [f filename]
  (with-open [rdr (io/reader (io/file filename))]
    (f (line-seq rdr))))

この配置により、f返される前に必要なだけ行 seq を実現する義務があることに注意してください (後でwith-open行 seq の基礎となるリーダーを閉じるため)。

この責任の分割を考えると、行処理関数は次のように---なります。最初の行は最初の非空白行である必要があり、すべての空白行はスキップされると仮定します (質問テキストのコードを使用する場合のように)。

(require '[clojure.string :as string])

(defn process-lines [lines]
  (let [ls (->> lines
                (map string/trim)
                (remove string/blank?))]
    (if (= (first ls) "---")
      (let [[front sep-and-body] (split-with #(not= "---" %) (next ls))]
        {:front (vec front) :body (vec (next sep-and-body))})
      {:body (vec ls)})))

vecすべての行が読み込まれ、ベクトルまたはベクトルのペアで返される呼び出しに注意してください(リーダーをすぐに閉じずにprocess-lineswithを使用できるようにするため)。process-file-with

ディスク上の実際のファイルからの行の読み取りは、一連の行の処理から切り離されているため、REPL でプロセスの後半部分を簡単にテストできます (もちろん、これは単体テストにすることもできます)。

;; could input this as a single string and split, of course
(def test-lines
  ["---"
   "key1: value1"
   "key2: value2"
   "---"
   ""
   "Body text paragraph 1"
   ""
   "Body text paragraph 2"
   ""
   "Body text paragraph 3"])

今すぐ関数を呼び出します:

user> (process-lines test-lines)
{:front ("key1: value1" "key2: value2"),
 :body ("Body text paragraph 1"
        "Body text paragraph 2"
        "Body text paragraph 3")}
于 2013-08-19T23:22:05.860 に答える