2011-01-13 14 views
6

मैं वर-args के रूप में, एक मैक्रो करने के लिए एक समारोह का वर-आर्ग भेजने के लिए अभी भी चाहते हैं में विस्तार करने के लिए कैसे। count=3; args=abcएक दृश्य (वर-args) अलग आइटम

(test-fn-calling-macro "a" "b" "c") के उत्पादन में है: count=1; args=("a" "b" "c") क्योंकि आर्ग मैक्रो के लिए एक एकल तर्क के रूप में भेजा जाता है

(defmacro test-macro 
[& args] 
`(println (str "count=" ~(count args) "; args=" [email protected]))) 

(defn test-fn-calling-macro 
[& args] 
(test-macro args)) 

(test-macro "a" "b" "c") के उत्पादन में जो मैं चाहता है: यहाँ मेरी कोड है। मैं कैसे आदेश 3 तर्क के साथ मैक्रो कॉल करने के लिए मेरे समारोह में इस आर्ग विस्तार कर सकते हैं?

मुझे लगता है मैं सिर्फ एक सरल मुख्य कार्य याद कर रहा हूँ लेकिन मैं इसे ढूँढने में सक्षम नहीं हूँ। धन्यवाद


संपादित 2 - मेरे "असली" कोड, नीचे संपादित करें अनुभाग में दिखाए इस तकनीक का उपयोग करने के लिए एक वैध स्थिति नहीं है।

रूप @Brian से कहा, मैक्रो xml-to-cass इस तरह एक समारोह के साथ बदला जा सकता है:

(defn xml-to-cass 
    [zipper table key attr & path] 
    (doseq [v (apply zf/xml-> zipper path)] (cass/set-attr! table key attr v))) 

संपादित करें - निम्न अनुभाग अपने मूल प्रश्न से परे चला जाता है लेकिन किसी भी अंतर्दृष्टि का स्वागत है

कोड ऊपर सिर्फ सबसे सरल मैं मेरी समस्या को इंगित करने के साथ आ सकता है है। मेरा असली कोड क्लोज-कैसंड्रा और ज़िप-फ़िल्टर से संबंधित है। यह ओवर-इंजीनियरिंग भी देख सकता है लेकिन यह सिर्फ एक खिलौना प्रोजेक्ट है और मैं एक ही समय में भाषा सीखने की कोशिश कर रहा हूं।

मैं कुछ एक्सएमएल एक कैसेंड्रा डेटाबेस में पाया mlb.com और डालने मूल्यों पर पाया पार्स करने के लिए चाहता हूँ। यहां मेरा कोड और इसके पीछे सोच है।

चरण 1 - समारोह जो ठीक काम करता है लेकिन कोड दोहराव

(ns stats.importer 
    (:require 
    [clojure.xml :as xml] 
    [clojure.zip :as zip] 
    [clojure.contrib.zip-filter.xml :as zf] 
    [cassandra.client :as cass])) 

(def root-url "http://gd2.mlb.com/components/game/mlb/year_2010/month_05/day_01/") 

(def games-table (cass/mk-cf-spec "localhost" 9160 "mlb-stats" "games")) 

(defn import-game-xml-1 
    "Import the content of xml into cassandra" 
    [game-dir] 
    (let [url (str root-url game-dir "game.xml") 
     zipper (zip/xml-zip (xml/parse url)) 
     game-id (.substring game-dir 4 (- (.length game-dir) 1))] 
    (doseq [v (zf/xml-> zipper (zf/attr :type))] (cass/set-attr! games-table game-id :type v)) 
    (doseq [v (zf/xml-> zipper (zf/attr :local_game_time))] (cass/set-attr! games-table game-id :local_game_time v)) 
    (doseq [v (zf/xml-> zipper :team [(zf/attr= :type "home")] (zf/attr :name_full))] (cass/set-attr! games-table game-id :home_team v)))) 

शामिल import-game-xml-1 पैरामीटर उदाहरण "gid_2010_05_01_colmlb_sfnmlb_1/" के लिए हो सकता है। मैं अपने डेटाबेस में कॉलमफैमिली गेम्स की कुंजी बनाने के लिए "gid_" और पिछला स्लैश हटा देता हूं।

मैंने पाया कि 3 doseq दोहराव का एक बहुत थे (और वहाँ अंतिम संस्करण में अधिक से अधिक 3 होना चाहिए)। तो मैक्रो का उपयोग कोड templating यहाँ उचित लग रहा था (मुझे ठीक कर लें मैं गलत हूँ)।

चरण 2 - कोड templating के लिए एक मैक्रो का परिचय (अभी भी काम करता है)

(defmacro xml-to-cass 
    [zipper table key attr & path] 
    `(doseq [v# (zf/xml-> ~zipper [email protected])] (cass/set-attr! ~table ~key ~attr v#))) 

(defn import-game-xml-2 
    "Import the content of xml into cassandra" 
    [game-dir] 
    (let [url (str root-url game-dir "game.xml") 
     zipper (zip/xml-zip (xml/parse url)) 
     game-id (.substring game-dir 4 (- (.length game-dir) 1))] 
    (xml-to-cass zipper games-table game-id :type (zf/attr :type)) 
    (xml-to-cass zipper games-table game-id :local_game_time (zf/attr :local_game_time)) 
    (xml-to-cass zipper games-table game-id :home_team :team [(zf/attr= :type "home")] (zf/attr :name_full)))) 

मुझे विश्वास है कि एक सुधार है, लेकिन मैं अभी भी हमेशा xml-to-cass मेरे कॉल में एक ही 3 मानकों पुन: उपयोग में कुछ दोहराव देखते हैं। यही कारण है कि मैंने उन लोगों की देखभाल करने के लिए एक मध्यवर्ती कार्य शुरू किया था।

चरण 3 - मैक्रो कॉल करने के लिए एक समारोह जोड़ा जा रहा है (यहां समस्या है)

(defn import-game-xml-3 
    "Import the content of xml into cassandra" 
    [game-dir] 
    (let [url (str root-url game-dir "game.xml") 
     zipper (zip/xml-zip (xml/parse url)) 
     game-id (.substring game-dir 4 (- (.length game-dir) 1)) 
     save-game-attr (fn[key path] (xml-to-cass zipper games-table game-id key path))] 
    (save-game-attr :type (zf/attr :type)) ; works well because path has only one element 
    (save-game-attr :local_game_time (zf/attr :local_game_time)) 
    (save-game-attr :home :team [(zf/attr= :type "home"] (zf/attr :name_full))))) ; FIXME this final line doesn't work 

उत्तर

4

यहां कुछ सरल कोड है जो रोशनी हो सकता है।

मैक्रोज़ कोड जनरेशन के बारे में हैं। यदि आप रनटाइम पर ऐसा करना चाहते हैं, किसी कारण से, तो आपको रनटाइम पर कोड बनाना और मूल्यांकन करना होगा। यह एक शक्तिशाली तकनीक हो सकती है।

(defmacro test-macro 
[& args] 
`(println (str "count=" ~(count args) "; args=" [email protected]))) 

(defn test-fn-calling-macro 
[& args] 
(test-macro args)) 

(defn test-fn-expanding-macro-at-runtime 
    [& args] 
    (eval (cons `test-macro args))) 

(defmacro test-macro-expanding-macro-at-compile-time 
    [& args] 
    (cons `test-macro args)) 

;; using the splicing notation 

(defmacro test-macro-expanding-macro-at-compile-time-2 
    [& args] 
    `(test-macro [email protected])) 

(defn test-fn-expanding-macro-at-runtime-2 
    [& args] 
    (eval `(test-macro [email protected]))) 



(test-macro "a" "b" "c") ;; count=3; args=abc nil 
(test-fn-calling-macro "a" "b" "c") ;; count=1; args=("a" "b" "c") nil 

(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil 
(test-macro-expanding-macro-at-compile-time "a" "b" "c") ; count=3; args=abc nil 
(test-macro-expanding-macro-at-compile-time-2 "a" "b" "c") ; count=3; args=abc nil 
(test-fn-expanding-macro-at-runtime "a" "b" "c") ; count=3; args=abc nil 

यदि उपरोक्त का विचार प्रबुद्ध साबित नहीं होता है, तो क्या मैं अपने कुछ ब्लॉग लेख सुझा सकता हूं?

इस एक मैं खरोंच से मैक्रो के माध्यम से जाने में, और कैसे विशेष रूप से clojure के काम:

http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-i-getting.html

और यह एक में मैं क्यों दिखाते रन-टाइम कोड पीढ़ी उपयोगी हो सकता है:

http://www.learningclojure.com/2010/09/clojure-faster-than-machine-code.html

+0

धन्यवाद लिखने के लिए समय निकालने के लिए बहुत कुछ। मैं अब प्रबुद्ध महसूस कर रहा हूँ। मैं निश्चित रूप से भी आपके ब्लॉग पोस्ट के माध्यम से जाना होगा। बहुत बुरा मैंने लिस्प को नहीं सीखा जब मैं एक जिद्दी वयस्क के बजाय एक बच्चा था। मुझे अपने दिमाग को इसके चारों ओर लेना मुश्किल है। लेकिन अब तक यह दर्द के लायक है। मैं हार नहीं मानूंगा और आशा करता हूं कि सोच को समझें। – Damien

+0

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

+1

वास्तव में इसे ग्रोक करने का तरीका एसआईसीपी के माध्यम से जाना और सभी अभ्यास करना है। http://mitpress.mit.edu/sicp/ वास्तव में यह शायद ही मैक्रोज़ का उल्लेख करता है, लेकिन उस समय के बारे में जहां आप योजना में एक योजना दुभाषिया लिखते हैं, आप समझेंगे कि मूल्यांकनकर्ता में क्या हो रहा है, और उस बिंदु पर मैक्रोज़ हैं एक मामूली अनुशासनिक। यहां तक ​​कि यदि आपका लक्ष्य क्लोजर या कुछ अन्य जटिल लिस्प में प्रोग्राम करना है, तो यह करना एक अच्छी बात है, क्योंकि योजना इतनी सरल है कि आप विचार देख सकते हैं। जावा सीखने की तरह थोड़ा पहले अगर आप वास्तव में करना चाहते हैं तो सी ++ है। –

2

एक समारोह के लिए अलग-अलग तर्क के रूप में एक संग्रह का उपयोग करने विशिष्ट तरीका उपयोग करने के लिए है (apply function my-list-o-args)

(defn test-not-a-macro [& args] 
    (print args)) 

(defn calls-the-not-a-macro [& args] 
    (apply test-not-a-macro args)) 

हालांकि आप आवेदन का उपयोग करने में सक्षम नहीं होंगे क्योंकि टेस्ट-मैक्रो एक मैक्रो है। इस समस्या को हल करने के लिए आपको फ़ंक्शन कॉल में टेस्ट मैक्रो को लपेटने की आवश्यकता होगी ताकि आप इसे लागू कर सकें।

(defmacro test-macro [& args] 
    `(println [email protected])) 

(defn calls-test-macro [& args] 
    (eval (concat '(test-macro) (args)))) ;you almost never need eval. 

(defn calls-calls-test-macro [& args] 
    (calls-test-macro args)) 

यह वास्तव में मैक्रोज़ लिखने के तरीकों में से एक का वास्तव में एक अच्छा उदाहरण है। (कुछ लोग कहते हैं कि वे साफ रूप से रचित नहीं हो सकते हैं, हालांकि मुझे लगता है कि यह एक असाधारण है)

+0

ps: मैं अपने आरईपीएल पर नहीं हूं इसलिए यदि यह टूटा हुआ है तो कृपया संपादित करें (या टिप्पणी करें और मैं घर आने पर ठीक कर दूंगा) –

+0

धन्यवाद। मैंने इस कोड को आजमाया लेकिन मुझे 'क्लासकास्ट अपवाद: clojure.lang.ArraySeq clojure.lang.IFn' पर नहीं डाला जा सकता है। कोई विचार क्यों? मैं वास्तव में हालांकि मेरे प्रश्न का सीधा जवाब होगा लेकिन यह आश्चर्यजनक रूप से जटिल दिखता है। मैंने अपने "असली" कोड के बारे में सभी विवरण प्रदान करने के लिए अपना प्रश्न संपादित किया। शायद यह मदद कर सकता है। बहुत बहुत धन्यवाद। – Damien

1

आपकी आवश्यकताओं को स्पष्ट नहीं है। मुझे नहीं लगता कि test-macro के लिए यहां एक मैक्रो क्यों आवश्यक है, जब तक कि आप अपने मैक्रो को अनचाहे रूपों मुद्रित करने का प्रयास नहीं कर रहे हैं।

इन कार्यों अपने अपेक्षित परिणाम प्रदान करते हैं, लेकिन यह क्योंकि आपके नमूना डेटा थी स्वयं का मूल्यांकन

(defn test-args 
    [& args] 
    (println (format "count=%d; args=%s" 
        (count args) 
        (apply str args)))) 

या

(defn test-args 
    [& args] 
    (print (format "count=%d; args=" (count args))) 
    (doseq [a args] 
    (pr a)) 
    (newline)) 

आप अन्य रूपों की कल्पना कर सकते एक ही परिणाम प्राप्त करने के लिए।

कोशिश कुछ के साथ कि समारोह है कि खुद के रूप में मूल्यांकित नहीं है, और परिणाम ध्यान दें बुला:

(test-args (+ 1 2) (+ 3 4)) 

थे आप (+1 2) तर्क के रूप में "37" या "मुद्रित (देखें करने के लिए देख + 3 4) "?

यदि आप इस विशेष समस्या को हल करने के विरोध में मैक्रोज़ और सामान्य रूप से उनके विस्तार के बारे में जानने की कोशिश कर रहे थे, तो कृपया आगे की जांच के लिए अपने प्रश्न को ट्यून करें।

+0

उत्तर के लिए धन्यवाद। Println के साथ मेरा कोड पूरी तरह कृत्रिम था और समस्या को दूर करने के लिए जितना संभव हो सके उतना आसान था। मैं अप्रासंगिक कोड वाले लोगों को परेशान नहीं करना चाहता था। लेकिन जब से आपने पूछा, मैंने अपना प्रश्न संपादित किया और सभी विवरण दिए कि मैंने क्यों सोचा कि मुझे मैक्रो की आवश्यकता है। मैं और अधिक विशिष्ट नहीं हो सकता; ओ) – Damien

2

मैक्रोज़ जादू नहीं हैं। वे संकलन-समय पर कोड को समकक्ष कोड में कनवर्ट करने के लिए एक तंत्र हैं; वे रन-टाइम पर उपयोग नहीं किए जाते हैं। जो दर्द आप महसूस कर रहे हैं वह इसलिए है क्योंकि आप ऐसा करने की कोशिश कर रहे हैं जिसे आपको करने की कोशिश नहीं करनी चाहिए।

मुझे प्रश्न में लाइब्रेरी नहीं पता है, लेकिन यदि cass/set-attr! एक फ़ंक्शन है, तो मुझे कोई कारण नहीं दिखता कि आपके द्वारा परिभाषित मैक्रो को मैक्रो क्यों होना चाहिए; यह इसके बजाय एक समारोह हो सकता है। यदि आप अपने मैक्रो को फ़ंक्शन के रूप में फिर से लिख सकते हैं तो आप वह कर सकते हैं जो आप करना चाहते हैं।

+0

बहुत धन्यवाद @ ब्रायन। तुम बिल्कुल सही हो मैं इसे एक समारोह के रूप में काम करने में सक्षम था। ऐसा लगता है कि मैंने अभी तक मैक्रोज़ के साथ खेलने के लिए पर्याप्त कौशल हासिल नहीं किए हैं। लेकिन मैं आपके जवाब को स्वीकार करने में संकोच करता हूं। क्या सैद्धांतिक मामला नहीं हो सका जहां किसी को वास्तव में फ़ंक्शन से मैक्रो तक "आगे" करने की आवश्यकता होगी? @ Eval' समारोह के साथ आर्थर का जवाब उन लोगों के लिए अधिक उपयुक्त हो सकता है। – Damien

संबंधित मुद्दे