2009-11-17 45 views
9

我對編程都是新手和老手 - 主要是我在工作中編寫了很多小的Perl腳本。當我想要學習Lisp時,Clojure就出來了,所以我想在不知道Java的情況下學習Clojure。這很艱難,但迄今爲止很有趣。新手在Clojure中轉換CSV文件

我見過幾個類似的問題的例子,但沒有什麼地圖可以映射到我的問題空間。是否有一種規範的方式來爲Clojure中的每個CSV文件行提取值列表?

下面是一些實際工作的Perl代碼;包括非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. 
} 

我Clojure中推敲出了一點點,並試圖在命令式風格湊齊了它:

; 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"))) 

我不希望最後一行實際工作,但它得到了重點。我在尋找的線沿線的東西:

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

感謝,

比爾
+2

'我($ x,$ y,$ z,$ p)= split /,/;' – 2009-11-17 04:42:11

+0

好點 - TIMTOWTDI。謝謝。 – 2009-11-17 04:57:51

回答

8

這裏有一種方法:

(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結合*out*,使得打印一切都會以文件名或您指定的流,而不是標準輸出。

使用def爲你使用它不地道。更好的方法是使用let。我正在使用解構來將每行的4個字段分配給4個與綁定的名稱;那麼你可以做那些你想做的。

如果你遍歷東西的副作用的目的(例如I/O)你通常應該去doseq。如果你想收集了各行成一個哈希地圖,做一些與他們以後,你可以使用for

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

正是我需要的!而且做得很好!直到現在,doseq對我來說沒有多大意義,現在我可以看到我也誤解了其他一些事情。我在ClojureBox中試過你的代碼,它工作正常。我也能夠將它包裝在一個函數中,並且工作得很好,所以這似乎使我走上了正確的軌道。再次感謝。 – 2009-11-18 01:45:49

15

請不要使用嵌套高清的。它不這樣做,你認爲它的確如此。 def始終是全球性的!對於當地人來說,用let代替。雖然庫函數很好理解,但這裏是編寫一般函數式編程的一些特性的版本,特別是clojure。

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

(defn translate-coords

可以通過(doc translate-coords)在REPL中查詢文檔字符串。適用於例如。爲所有核心職能。所以提供一個是個好主意。

"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)]

我們將*out*流臨時綁定到輸出文件。因此,綁定內的任何打印都將打印到該文件。

(binding [*out* out]

map方法:取序列和給定函數應用於每個元素並返回結果的序列。 #()是匿名函數的簡寫符號。它需要一個參數,填寫在%doseq基本上是一個輸入循環。既然我們這樣做的副作用(即打印到文件),doseq是正確的構造。經驗法則:map:lazy =>結果,doseq:eager =>副作用。

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

println在該行的末尾花費的\n照顧。 interpose接受seq並在其元素之間添加第一個參數(在我們的例子中是「」)。 (apply str [1 2 3])相當於(str 1 2 3),並且可用於動態構造函數調用。 ->>是clojure中一個相對較新的宏,它有助於閱讀。這意味着「採取第一個參數,並將其作爲最後一項添加到函數調用」。給定->>相當於:(println (apply str (interpose " " (translator coords))))。 (編輯:另一個注意:由於分隔符是\space,我們可以在這裏寫(apply println (translator coords)),但interpose版本允許參數化分隔符,就像我們用翻譯器功能做的那樣,而短版本將硬鏈接\space。)

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

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

這裏我們使用解構(注意雙[[]])。這意味着該函數的參數是可以變成seq的東西,例如。一個向量或一個列表。將第一個元素綁定到y,將第二個元素綁定到x等等。

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

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

這裏又少波濤洶涌:

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

希望這有助於。

編輯:對於CSV閱讀你可能想要OpenCSV之類的東西。

+1

感謝您的教程 - 那裏有很多有用的信息,需要我花一些時間來消化。我模仿了你自己在這裏使用的功能,它的功能就像魅力一樣。再次感謝! – 2009-11-18 01:52:43