大きなjsonファイル(3GB)を解析し、このファイルの各行に対してハッシュマップを返したいとします。私の直感は、トランスデューサを使って行ごとにファイルを処理し、いくつかの選択されたフィールド(ファイルのバイトの> 5%)を持つベクトルを構築することでした。私はJVisualVMとプロセスを可視化すると、ヒープが終わっ成長Clojure Tranducersを使用して大きなファイルを解析する:OutOfMemoryエラー
(defn load-with!
"Load a file using a parser, a structure and a transducer."
[parser structure xform path]
(with-open [r (clojure.java.io/reader path)]
(into structure xform (parser r))))
(def xf (map #(get-in % ["experiments" "results"])))
(def parser (comp (partial map cheshire.core/parse-string) line-seq))
(load-with! parser (vector) xf "file.json")
file.json
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
parser.clj:
ただし、次のコードは、のOutOfMemory例外をスロープロセスがクラッシュする前に25 GBを超えています。
この場合、トランスデューサは適切ですか?より良い選択肢がありますか?
私の要件の1つは、機能の最後に新しい構造を返すことです。したがって、私はdosqを使用してファイルをインプレースで処理することはできません。
さらに、ファイル形式に従ってパーサーとトランスデューサを変更する必要があります。
ありがとうございました!
私はコードを完全に理解していません。パーサーの役割は何ですか?渡されたものの未使用のようです。また、 '(r)'という表現はおそらくあなたが望むものではなく、読者を関数と呼びます。 –
トランスデューサがなぜ役立つのかわかりません。トランスデューサは、データに対して一連の操作を実行する場合に便利です。トランスデューサを使用すると、捨てられる中間データ構造を作成することを避けることができます。このコードは、 'get-in'をマップします。 'into'は非遅延型です。あなたは遅れてファイルを処理できますか? 'for'、' map'、または 'sequence'トランスデューサ関数を使って、怠惰なシーケンスのマップエントリを作成できますか?それらが正しく処理されていれば、すべてのファイルの内容をメモリに保存せずにそれぞれを処理することができます。 – Mars
パーサー/トランスデューサの目的は、ファイルフォーマット(例えば、json、csv ...)およびファイル内のベンダフォーマットに従って容易に作業を適合させることである。 – fmind