2009-11-17 25 views
9

Tôi vừa mới và cũ để lập trình - chủ yếu là tôi chỉ viết rất nhiều tập lệnh Perl nhỏ tại nơi làm việc. Clojure xuất hiện ngay khi tôi muốn tìm hiểu Lisp, vì vậy tôi đang cố gắng tìm hiểu Clojure mà không biết Java. Đó là khó khăn, nhưng nó đã được vui vẻ cho đến nay.Người mới chuyển đổi tệp CSV ở Clojure

Tôi đã nhìn thấy một số ví dụ về các vấn đề tương tự như của tôi, nhưng không có gì mà bản đồ hoàn toàn không gian vấn đề của tôi. Có cách nào để trích xuất danh sách giá trị cho mỗi dòng của tệp CSV trong Clojure không?

Dưới đây là một số mã Perl hoạt động thực tế; ý kiến ​​bao gồm cho phi Perlers:

# convert_survey_to_cartography.pl 
open INFILE, "< coords.csv";  # Input format "Northing,Easting,Elevation,PointID" 
open OUTFILE, "> coords.txt";  # Output format "PointID X Y Z". 
while (<INFILE>) {     # Read line by line; line bound to $_ as a string. 
    chomp $_;      # Strips out each line's <CR><LF> chars. 
    @fields = split /,/, $_;  # Extract the line's field values into a list. 
    $y = $fields[0];    # y = Northing 
    $x = $fields[1];    # x = Easting 
    $z = $fields[2];    # z = Elevation 
    $p = $fields[3];    # p = PointID 
    print OUTFILE "$p $x $y $z\n" # New file, changed field order, different delimiter. 
} 

Tôi đã bối rối ra một chút trong Clojure và cố gắng để rai nó cùng nhau trong một phong cách bắt buộc:

; convert-survey-to-cartography.clj 
(use 'clojure.contrib.duck-streams) 
(let 
    [infile "coords.csv" outfile "coords.txt"] 
    (with-open [rdr (reader infile)] 
    (def coord (line-seq rdr)) 
    (...then a miracle occurs...) 
    (write-lines outfile ":x :y :z :p"))) 

Tôi không mong đợi dòng cuối cùng để thực sự hoạt động, nhưng nó được điểm qua. Tôi đang tìm một cái gì đó dọc theo dòng:

(def values (interleave (:p :y :x :z) (re-split #"," coord))) 

Cảm ơn, Bill

+2

'my ($ x, $ y, $ z, $ p) = chia /, /;' –

+0

Điểm tốt - TIMTOWTDI. Cảm ơn. –

Trả lời

8

Dưới đây là một cách:

(use '(clojure.contrib duck-streams str-utils))     ;;' 
(with-out-writer "coords.txt" 
    (doseq [line (read-lines "coords.csv")] 
    (let [[x y z p] (re-split #"," line)] 
     (println (str-join \space [p x y z]))))) 

with-out-writer liên kết *out* như vậy mà tất cả mọi thứ bạn in sẽ đi đến tên tập tin hoặc luồng bạn chỉ định, thay vì đầu ra tiêu chuẩn.

Sử dụng def vì bạn đang sử dụng nó không phải là thành ngữ. Cách tốt hơn là sử dụng let. Tôi đang sử dụng destructuring để gán 4 trường của mỗi dòng cho 4 let tên có liên quan; thì bạn có thể làm những gì bạn muốn với những thứ đó.

Nếu bạn đang lặp lại điều gì đó vì mục đích của các tác dụng phụ (ví dụ: I/O), bạn thường nên truy cập doseq. Nếu bạn muốn thu thập từng dòng vào bản đồ băm và thực hiện điều gì đó với chúng sau này, bạn có thể sử dụng for:

(with-out-writer "coords.txt" 
    (for [line (read-lines "coords.csv")] 
    (let [fields (re-split #"," line)] 
     (zipmap [:x :y :z :p] fields)))) 
+0

Chính xác những gì tôi cần! Và cũng được tao nhã! liều lượng đã không có ý nghĩa nhiều với tôi cho đến bây giờ, và tôi có thể thấy bây giờ mà tôi hiểu lầm một vài điều khác nữa. Tôi đã thử mã của bạn trong ClojureBox và nó đã hoạt động; Tôi cũng có thể quấn nó lên trong một chức năng và nó hoạt động tốt, vì vậy điều này dường như đã giúp tôi đi đúng hướng. Cảm ơn một lần nữa. –

15

Vui lòng không sử dụng tính năng def lồng nhau. Nó không làm, những gì bạn nghĩ rằng nó. def luôn là toàn cầu! Thay vào đó, để người dân địa phương sử dụng thay thế. Trong khi các chức năng thư viện rất hay để biết, ở đây một phiên bản phối hợp một số tính năng của lập trình hàm nói chung và clojure nói riêng.

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader) 

(defn translate-coords

Tài liệu có thể được truy vấn trong REPL qua (doc translation-coords). Làm việc ví dụ. cho tất cả các chức năng cốt lõi. Vì vậy, cung cấp một là một ý tưởng tốt.

"Reads coordinates from infile, translates them with the given 
    translator and writes the result to outfile."

dịch là một hàm (có thể ẩn danh) trích xuất bản dịch từ bản mẫu xung quanh. Vì vậy, chúng tôi có thể tái sử dụng chức năng này với các quy tắc chuyển đổi khác nhau. Các gợi ý loại ở đây tránh sự phản ánh cho các nhà xây dựng.

[translator #^String infile #^String outfile]

Mở tệp. với mở sẽ cẩn thận, rằng các tập tin được đóng lại khi cơ thể của nó là trái. Có thể là thông qua "thả xuống đáy" bình thường hoặc là thông qua một ngoại lệ được ném.

(with-open [in (BufferedReader. (FileReader. infile)) 
       out (FileWriter. outfile)]

Chúng tôi ràng buộc luồng *out* tạm thời vào tệp đầu ra. Vì vậy, bất kỳ bản in nào bên trong liên kết sẽ in ra tệp.

(binding [*out* out]

map có nghĩa là: lấy seq và áp dụng hàm cho mỗi phần tử và trả về kết quả. #() là ký pháp viết tắt của một hàm ẩn danh. Phải mất một đối số, được điền vào tại số %. Các doseq về cơ bản là một vòng lặp trên đầu vào. Vì chúng ta làm điều đó cho các tác dụng phụ (cụ thể là in một tệp), doseq là cấu trúc đúng. Quy tắc: map: lazy => cho kết quả, doseq: eager => cho các tác dụng phụ.

 (doseq [coords (map #(.split % ",") (line-seq in))]

println sẽ chăm sóc cho \n ở cuối dòng. interpose lấy seq và thêm đối số đầu tiên (trong trường hợp của chúng tôi "") giữa các phần tử của nó. (apply str [1 2 3]) tương đương với (str 1 2 3) và hữu ích để xây dựng các cuộc gọi hàm động. Các ->> là một vĩ mô tương đối mới trong clojure, giúp một chút với khả năng đọc. Nó có nghĩa là "lấy đối số đầu tiên và thêm nó làm mục cuối cùng cho cuộc gọi hàm". Số ->> nhất định tương đương với: (println (apply str (interpose " " (translator coords)))). (Chỉnh sửa: Một lưu ý: kể từ khi tách là \space, chúng tôi có thể viết ở đây cũng như (apply println (translator coords)), nhưng phiên bản interpose cho phép cũng parametrize tách như chúng tôi đã làm với chức năng dịch, trong khi phiên bản ngắn sẽ hardwire \space.)

 (->> (translator coords) 
      (interpose " ") 
      (apply str) 
      println))))) 

(defn survey->cartography-format 
    "Translate coords in survey format to cartography format."

Ở đây chúng tôi sử dụng destructuring (lưu ý đôi [[]]). Nó có nghĩa là đối số cho hàm là cái gì đó có thể được biến thành một seq, ví dụ. một vector hoặc một danh sách. Liên kết phần tử đầu tiên với số y, phần tử thứ hai là x, v.v.

[[y x z p]] 
    [p x y z]) 

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")

đây một lần nữa ít choppy:

(import 'java.io.FileWriter 'java.io.FileReader 'java.io.BufferedReader) 

(defn translate-coords 
    "Reads coordinates from infile, translates them with the given 
    translator and writes the result to outfile." 
    [translator #^String infile #^String outfile] 
    (with-open [in (BufferedReader. (FileReader. infile)) 
       out (FileWriter. outfile)] 
    (binding [*out* out] 
     (doseq [coords (map #(.split % ",") (line-seq in))] 
     (->> (translator coords) 
      (interpose " ") 
      (apply str) 
      println))))) 

(defn survey->cartography-format 
    "Translate coords in survey format to cartography format." 
    [[y x z p]] 
    [p x y z]) 

(translate-coords survey->cartography-format "survey_coords.txt" "cartography_coords.txt")

Hope this helps.

Chỉnh sửa: Để đọc CSV, bạn có thể muốn một cái gì đó như OpenCSV.

+1

Cảm ơn các hướng dẫn - có rất nhiều thông tin hữu ích trong đó sẽ đưa tôi một thời gian để tiêu hóa. Tôi đã lập mô hình một chức năng của riêng tôi trên một cái mà bạn đã sử dụng ở đây và nó hoạt động như một sự quyến rũ. Cảm ơn một lần nữa! –

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