2012-08-16 36 views
7

Tôi đang cố gắng lấy 5 dòng theo số dòng của chúng từ một tệp lớn (> 1GB) với Clojure. Tôi gần như ở đó nhưng đang thấy một số điều kỳ lạ, và tôi muốn hiểu những gì đang xảy ra.Trích xuất một cách dễ dàng các dòng từ tệp lớn

Cho đến nay tôi đã có:

(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)))) 

Bây giờ, (read-lines "my-file" [0]) công trình mà không có một vấn đề. Tuy nhiên, đi qua trong [0 1] mang lại cho tôi stacktrace sau:

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] 

Dường như dòng suối đang được đóng trước khi tôi có thể đọc dòng thứ hai từ tập tin. Thật thú vị, nếu tôi tự kéo ra một dòng từ tệp có nội dung như (nth lines 200), cuộc gọi multi-nth hoạt động với tất cả các giá trị < = 200.

Bất kỳ ý tưởng gì đang xảy ra?

Trả lời

9

bản đồ (và dòng-seq) trả về các chuỗi lười biếng, vì vậy không có dòng nào nhất thiết phải được đọc vào thời điểm bạn gọi tới trả lại mở, đóng tập tin đó.

về cơ bản, bạn cần phải nhận ra giá trị toàn bộ lợi nhuận trước với mở trở lại, mà bạn có thể sử dụng 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))))) 

hoặc một cái gì đó như thế. hãy nhớ rằng đa phần thứ ba của bạn giữ trên đầu dòng seq trong khi tìm kiếm các dòng được chỉ định, có nghĩa là nó sẽ giữ tất cả các dòng cho đến khi dòng được chỉ định cuối cùng trong bộ nhớ - và sử dụng thứ n như vậy có nghĩa là bạn đang lặp lại dòng-seq nhiều lần cho mỗi chỉ mục - bạn sẽ muốn sửa lỗi đó.

cập nhật:

Điều gì đó tương tự như vậy sẽ hiệu quả. Đó là một chút xấu xí hơn tôi thích nhưng nó cho thấy nguyên tắc, tôi nghĩ: Lưu ý rằng các chỉ số ở đây cần phải là một đặt.

(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 
+0

Tốt điểm. Tôi có cần sử dụng các cuộc gọi phương thức truy cập ngẫu nhiên Java cấp thấp hơn để có được đúng không? –

+0

Tôi nghĩ rằng bạn có thể nhận được mã rõ ràng bằng cách sử dụng lập chỉ mục bản đồ và bộ lọc. Tôi sẽ cập nhật sau một phút ... –

+0

Ah được rồi, thật tuyệt. Nếu đó là cú pháp xấu hơn bạn muốn, bạn có thể sử dụng giữ lập chỉ mục và có thể ngưng tụ chức năng lọc. Nhìn vào nó bây giờ ... –

5

with-file đóng tệp khi cơ thể đã được thi hành. Vì vậy, một khi các multi-nth đã được thực hiện tập tin được đóng lại, có nghĩa là bạn kết thúc với một chuỗi lười biếng trỏ đến một tập tin đóng cửa.

(read-lines "my-file" [0]) hoạt động vì chỉ có phần tử đầu tiên của chuỗi lười được nhận ra.

Để khắc phục vấn đề này, bạn cần phải buộc các chuỗi được thực hiện với doall:

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

Đối với một lời giải thích rất chi tiết thấy https://stackoverflow.com/a/10462159/151650

+0

Ah. Có ý nghĩa. Cảm ơn! –

Các vấn đề liên quan