我需要编写一个函数,根据字段的值将记录拆分到不同的文件中。例如,给定输入:
[
["Paul" "Smith" 35]
["Jason" "Nielsen" 39]
["Charles" "Brown" 22]
]我们最终得到一个包含"Paul Smith 35"的"Paul"文件、包含"Jason"的文件、包含"Jason Nielsen 39"的文件等。
我事先不知道作者的名字,所以我需要为作者保留参考,这样我才能在最后关闭他们。
我能想到的最好的办法就是用一个裁判来留住作者,就像这样:
(defn write-split [records]
(let [out-dir (io/file "/tmp/test/")
open-files (ref {})]
(try
(.mkdirs out-dir)
(dorun
(for [[fst lst age :as rec] records]
(binding [*out* (or
(@open-files fst)
(dosync
(alter open-files assoc fst (io/writer (str out-dir "/" fst)))
(@open-files fst)))]
(println (apply str (interpose " " rec))))))
(finally (dorun (map #(.close %) (vals @open-files)))))))这很有效,但感觉很糟糕,更重要的是,堆用完了,即使我只有五个输出文件,它们一开始就是打开的。似乎有什么东西被保留了下来...
有没有人能想到一种更实用、更像Clojure的解决方案?
编辑:输入很大。潜在的千兆字节数据,因此内存效率的重要性,以及不愿在每次写入后关闭文件。
发布于 2012-01-06 05:14:20
(use '[clojure.string :only (join)])
(defn write-records! [records]
(let [writers (atom {})]
(try
(doseq [[filename :as record] records]
(let [w (or (get @writers
filename)
(get (swap! writers assoc filename (writer filename)) filename))]
(.write w (str (join " " record) "\n"))))
(finally (dorun (map #(.close (second %)) @writers))
(reset! writers {})))))发布于 2012-01-06 00:15:13
我想知道您的堆耗尽的问题是否与在for中使用绑定有关。看起来你的代码需要为每一条记录创建一个新的绑定,也许旧的会被保留下来。(关于这一点,我可能完全错了,clojure绑定对我来说是一门黑暗的艺术)。
您可以考虑让主记录排序代码将数据放到队列中(可能每个逻辑文件一个)。然后让一些“工作者”(可能是关闭适当的out绑定的编写器函数)使用java执行器库中的某些东西从队列中拉出。(这个问题:"Sleeping a thread inside an ExecutorService (Java/Clojure)"可能会提供一些提示。)
您仍然需要以某种方式优雅地关闭工作进程和关闭文件。(另一个问题"Clojure agents consuming from a queue"可能会提出一种方法。)
祝好运!必须将无限数据上的序列抽象与文件系统的不可避免的命令式状态联系起来并不是微不足道的(但希望在Clojure中比在其他语言中更简单)。
发布于 2012-01-05 20:48:12
with-open可以为您处理文件的关闭。
(ns sandbox.core
(:require [clojure.java.io :as io]))
(def data [["Paul" "Smith" 35]
["Jason" "Nielsen" 39]
["Charles" "Brown" 22]])
(doseq [record data]
(with-open [w (io/writer (first record))]
(binding [*out* w]
(apply println record))))根据您所做的编辑,您不会因为性能原因而一直打开和关闭文件。一种方法是将写入器保存在缓存中。下面的方法使用core.memoize来记录get-writer函数。写入所有记录后,将关闭缓存的写入器。
(defn write-data [data]
(let [get-writer (memoize/memo #(io/writer % :append true))]
(try
(doseq [record data]
(let [w (get-writer (first record))]
(binding [*out* w]
(apply println record))))
(finally
(dorun (map #(.close %)
(vals (memoize/snapshot get-writer))))))))https://stackoverflow.com/questions/8741514
复制相似问题