懒惰地从大文件中提取线条

Dav*_* J. 7 clojure stream-processing lazy-evaluation

我试图通过Clojure的大(> 1GB)文件中的行号抓取5行.我快到了,但看到了一些奇怪的事情,我想知道发生了什么.

到目前为止我有:

(defn multi-nth [values indices]
  (map (partial nth values) indices))

(defn read-lines [file indices]
  (with-open [rdr (clojure.java.io/reader file)]
    (let [lines (line-seq rdr)]
      (multi-nth lines indices))))
Run Code Online (Sandbox Code Playgroud)

现在,(read-lines "my-file" [0])工作没有问题.但是,传入[0 1]给我以下堆栈跟踪:

java.lang.RuntimeException: java.io.IOException: Stream closed
        Util.java:165 clojure.lang.Util.runtimeException
      LazySeq.java:51 clojure.lang.LazySeq.sval
      LazySeq.java:60 clojure.lang.LazySeq.seq
         Cons.java:39 clojure.lang.Cons.next
          RT.java:769 clojure.lang.RT.nthFrom
          RT.java:742 clojure.lang.RT.nth
         core.clj:832 clojure.core/nth
         AFn.java:163 clojure.lang.AFn.applyToHelper
         AFn.java:151 clojure.lang.AFn.applyTo
         core.clj:602 clojure.core/apply
        core.clj:2341 clojure.core/partial[fn]
      RestFn.java:408 clojure.lang.RestFn.invoke
        core.clj:2430 clojure.core/map[fn]
Run Code Online (Sandbox Code Playgroud)

在我从文件中读取第二行之前,似乎关闭了流.有趣的是,如果我手动从文件中提取一行,那么(nth lines 200)multi-nth调用适用于所有值<= 200.

知道发生了什么事吗?

Joo*_*aat 9

map(和line-seq)返回延迟序列,因此在调用with-open返回时,不会读取任何行,这会关闭文件.

基本上,你需要在with-open返回之前实现整个返回值,你可以使用doall:

(defn multi-nth [values indices]
  (map (partial nth values) indices))

(defn read-lines [file indices]
  (with-open [rdr (clojure.java.io/reader file)]
    (let [lines (line-seq rdr)]
      (doall (multi-nth lines indices)))))
Run Code Online (Sandbox Code Playgroud)

或类似的东西.请记住,在搜索指定的行时,你的多个nth保持行seq的头部,这意味着它将保持所有行直到最后指定的行在内存中 - 并且使用nth就像这意味着你为每个索引反复单步执行line-seq - 你需要修复它.

更新:

这样的东西会起作用.它比我喜欢的有点丑,但它显示了原理,我想:注意这里的索引需要是一个集合.

(defn multi-nth [values indices]
 (keep 
   (fn [[number line]] 
     (if (contains? indices number) 
       line))
   (map-indexed vector values)))

(multi-nth '(a b c d e) #{2 3})
  => c d
Run Code Online (Sandbox Code Playgroud)


Dan*_*ero 5

with-file一旦执行了正文,就会关闭文件.因此,一旦multi-nth执行完毕,文件就会关闭,这意味着您最终会得到一个指向已关闭文件的延迟序列.

(read-lines "my-file" [0]) 因为只实现了延迟序列的第一个元素,所以可以正常工作.

要解决此问题,您需要强制执行序列doall:

(defn multi-nth [values indices]
  (doall (map (partial nth values) indices)))
Run Code Online (Sandbox Code Playgroud)

有关详细说明,请参阅/sf/answers/732351161/