2009-11-17 6 views
9

में सीएसवी फाइलों को बदलने वाली न्यूबी ने प्रोग्रामिंग के लिए नए और पुराने दोनों हैं - ज्यादातर मैं काम पर बहुत छोटी पर्ल स्क्रिप्ट लिखता हूं। जब मैं लिस्प सीखना चाहता था तो क्लोजर बाहर आया, इसलिए मैं जावा को जानने के बिना क्लोजर सीखने की कोशिश कर रहा हूं। यह कठिन है, लेकिन अब तक यह मजेदार रहा है।क्लोजर

मैंने अपनी समस्याओं के कई उदाहरण देखे हैं, लेकिन कुछ भी नहीं जो मेरी समस्या की जगह पर काफी नक्शा है। Clojure में एक CSV फ़ाइल की प्रत्येक पंक्ति के लिए मूल्यों की सूचियों को निकालने का एक कैननिक तरीका है?

यहां कुछ वास्तविक कार्यरत पर्ल कोड है;

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

मैं अंतिम पंक्ति की उम्मीद नहीं है टिप्पणियां गैर Perlers के लिए शामिल वास्तव में काम करने के लिए, लेकिन यह बिंदु पार हो जाता है। मैं की तर्ज पर कुछ के लिए देख रहा हूँ:

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

धन्यवाद, बिल

+2

'मेरा ($ x, $ y, $ z, $ p) = विभाजन /, /;' –

+0

अच्छा बिंदु - TIMTOWTDI। धन्यवाद। –

उत्तर

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 let -बाउंड नामों को असाइन करने के लिए विनाशकारी का उपयोग कर रहा हूं; तो आप उन लोगों के साथ कर सकते हैं जो आप चाहते हैं।

यदि आप साइड इफेक्ट्स (उदाहरण के लिए 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

बिल्कुल मुझे क्या चाहिए! और सुंदर ढंग से भी किया! डोसक ने अब तक मुझे ज्यादा समझ नहीं लिया, और अब मैं देख सकता हूं कि मैंने कुछ और चीजों को भी गलत समझा। मैंने क्लोजरबॉक्स में अपना कोड आजमाया और यह काम किया; मैं इसे एक समारोह में लपेटने में भी सक्षम था और यह भी काम करता था, इसलिए ऐसा लगता है कि मुझे सही रास्ते पर रखा गया है। एक बार फिर धन्यवाद। –

15

कृपया नेस्टेड डीईएफ़ के प्रयोग नहीं करते। ऐसा नहीं करता है, जो आपको लगता है वह करता है। डीफ हमेशा वैश्विक है! स्थानीय लोगों के लिए उपयोग करते हैं। जबकि लाइब्रेरी फ़ंक्शंस जानना अच्छा होता है, यहां एक संस्करण सामान्य रूप से कार्यात्मक प्रोग्रामिंग की कुछ विशेषताओं और विशेष रूप से क्लोजर को व्यवस्थित करता है।

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

हम आउटपुट फ़ाइल के लिए अस्थायी रूप से *out* धारा बाँध। तो बाध्यकारी के अंदर कोई भी प्रिंट फ़ाइल को प्रिंट करेगा।

(binding [*out* out]

map साधन: seq लेने के लिए और हर तत्व को दिया समारोह लागू करते हैं और परिणामों की seq लौट आते हैं। #() एक अज्ञात फ़ंक्शन के लिए एक संक्षिप्त हाथ नोटेशन है। यह एक तर्क लेता है, जो % पर भरा हुआ है। doseq मूल रूप से इनपुट पर एक लूप है। चूंकि हम साइड इफेक्ट्स (अर्थात् फाइल को प्रिंट करना) के लिए करते हैं, doseq सही निर्माण है। अंगूठे का नियम: map: आलसी => परिणामस्वरूप, doseq: eager => दुष्प्रभावों के लिए।

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

println पंक्ति के अंत में \n के लिए ख्याल रखता है। interpose सीईसी लेता है और इसके तत्वों के बीच पहला तर्क (हमारे मामले में "") जोड़ता है। (apply str [1 2 3])(str 1 2 3) के बराबर है और गतिशील रूप से फ़ंक्शन कॉल बनाने के लिए उपयोगी है। ->> क्लोजर में अपेक्षाकृत नया मैक्रो है, जो पठनीयता के साथ थोड़ा सा मदद करता है। इसका मतलब है "पहला तर्क लें और इसे फंक्शन कॉल में अंतिम आइटम के रूप में जोड़ें"। दिया गया ->> बराबर है: (println (apply str (interpose " " (translator coords))))। (संपादित करें: एक और ध्यान दें: के बाद से विभाजक \space है लिखते हैं, हम यहाँ कर सकते थे बस के रूप में अच्छी तरह से (apply println (translator coords)), लेकिन interpose संस्करण के रूप में हम अनुवादक समारोह के साथ किया था भी विभाजक parametrize है, जबकि लघु संस्करण \space hardwire हैं अनुमति देता है।)

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

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

यहां हम विनाशकारी का उपयोग करते हैं (डबल [[]] नोट करें)। इसका मतलब है कि समारोह के लिए तर्क कुछ ऐसा है जिसे एक सीक में बदल दिया जा सकता है, उदाहरण के लिए। एक वेक्टर या एक सूची। पहले तत्व को 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")

आशा इस मदद करता है।

संपादित करें: सीएसवी पढ़ने के लिए शायद आप OpenCSV की तरह कुछ चाहते हैं।

+1

ट्यूटोरियल के लिए धन्यवाद - वहां बहुत सारी उपयोगी जानकारी है जो मुझे पचाने में कुछ समय लगेगी। मैंने आपके द्वारा यहां उपयोग किए जाने वाले एक फ़ंक्शन का मॉडल बनाया और यह एक आकर्षण की तरह काम करता था। एक बार फिर धन्यवाद! –