2012-02-14 16 views
23

clojure में, apply मैक्रो पर लागू नहीं किया जा सकता है। उदाहरण के लिए (apply and [true false]) एक अपवाद उठाता है। मैं निम्नलिखित तरीके को के बारे में सोच रहा था: पहली नज़र मेंक्लोजर में, किसी सूची में मैक्रो कैसे लागू करें?

(defmacro apply-macro[func args] `(~func [email protected])) 

, यह बहुत अच्छी तरह से काम करने के लिए लग रहा था:

(apply-macro and [true 5]); 5 
(apply-macro and [true 5 0]); 0 
(let [a 0] (apply-macro and [true a])); 0 

लेकिन, जब मैं इसे करने के लिए एक चर है कि एक वेक्टर के लिए अंक पारित कर दिया, यह ढह गई।

(let [a [true]] (apply-macro and a)); java.lang.IllegalArgumentException: 
    ;Don't know how to create ISeq from: clojure.lang.Symbol 

क्या निराशा होती है !!!!

कोई विचार apply-macro को ठीक करने का तरीका है?

उत्तर

24

समस्या यह है कि a संकलन समय पर केवल एक प्रतीक है। तो संकलन-समय मैक्रो के लिए यह देखने का कोई तरीका नहीं है कि इसमें क्या शामिल है और आवश्यक विस्तार करें। नतीजतन, आपको eval का उपयोग कर रन-टाइम पर मैक्रो का विस्तार करने की आवश्यकता है।

(defmacro functionize [macro] 
    `(fn [& args#] (eval (cons '~macro args#)))) 

(let [a [true]] (apply (functionize and) a)) 
=> true 

आप चाहें, तो आप भी निर्धारित कर सकते हैं लागू होते हैं:

एक तरह से यह करने के लिए सिर्फ एक समारोह है कि eval कहता है, जो इस काम "functionize" मैक्रो साथ किया जा सकता में मैक्रो रैप करने के लिए है functionize के मामले में -macro:

(defmacro apply-macro [macro args] 
    `(apply (functionize ~macro) ~args)) 

(let [a [true false]] (apply-macro and a)) 
=> false 

यह सब कहा, मैं अब भी लगता है तो सबसे अच्छा होगा जब वे वास्तव में जरूरत नहीं होती है पूरी तरह से मैक्रो से बचने के लिए है: वे अतिरिक्त जटिलता जोड़ सकते हैं और सबसे अच्छा मामले ऐसे हैं जहां के लिए आरक्षित हैं आपको वास्तव में संकलन समय कोड पीढ़ी की आवश्यकता है। इस मामले में आप नहीं करते: Alex Taggart's answer किसी भी मैक्रोज़ के बिना समान उद्देश्य प्राप्त करने का एक अच्छा उदाहरण देता है, जो अधिकतर स्थितियों में शायद अधिक उपयुक्त है।

+0

@YehonathanSharvit के लिए इस्तेमाल किया जा सकता जोड़ा जा सकता है: बात यह है कि '(लागू-स्थूल और ए) '** ** ** ** पर **' तक विस्तारित हो जाता है (और <फॉर्म में विभाजित>), लेकिन 'ए' केवल ** मूल्यांकन ** समय पर परिभाषित हो जाता है। तो 'ए' की 'unquote-splicing' (यानी' ~ @ ए') विफल हो जाती है, क्योंकि 'ए' अभी तक परिभाषित नहीं किया गया है। – liwp

+0

निश्चित रूप से, 'ए' रन-टाइम पर एक वेक्टर है। लेकिन यदि आप संकलन समय पर 'ए' की सामग्री के साथ '(और ....)' फ़ॉर्म बनाना चाहते हैं, तो इसका कोई उपयोग नहीं है, क्योंकि संकलक 'ए' की सामग्री नहीं देख सकता है। इसलिए आपको पता चलने के बाद कि आपको 'ए' में क्या पता है, इसलिए eval की आवश्यकता है, तो आपको रनटाइम पर संकलक * फिर से कॉल करने की आवश्यकता है। यह थोड़ा उलझन में है लेकिन उम्मीद है कि तर्क समझ में आता है ..... – mikera

+0

'फंक्शनक्शन' बहुत अच्छा लगता है !!! क्या आप लेखक हैं? क्या कोई सीमाएं हैं? – viebel

27

आप नहीं करते हैं।

मैक्रो मूल्यांकन के दौरान विस्तार/समय संकलन रनटाइम पर नहीं, है, इस प्रकार केवल जानकारी वे उपयोग कर सकते आर्ग, में पारित यह नहीं कि आर्ग क्रम पर करने के लिए मूल्यांकन कर रहे हैं। यही कारण है कि एक शाब्दिक वेक्टर काम करता है, क्योंकि उस शाब्दिक वेक्टर संकलन समय पर है, लेकिन a सिर्फ एक प्रतीक है; यह केवल रनटाइम पर एक वेक्टर का मूल्यांकन करेगा।

and जैसे सूचियों के लिए व्यवहार, (every? identity coll) का उपयोग करें।

or जैसे सूचियों के लिए व्यवहार, (some identity coll) का उपयोग करें।

1

बेशक, उचित उत्तर यह न करें। लेकिन, के बाद से मैं एक अच्छा हैक का विरोध नहीं कर सकते हैं:

(defmacro apply-macro 
    "Applies macro to the argument list formed by prepending intervening 
    arguments to args." 
    {:arglists '([macro args] 
       [macro x args] 
       [macro x y args] 
       [macro x y z args] 
       [macro a b c d & args])} 
    [macro & args+rest] 
    (let [args (butlast args+rest) 
     rest-args (eval (last args+rest))] 
    `(eval 
     (apply (deref (var ~macro)) 
       '(~macro [email protected] [email protected]) 
       nil 
       [email protected](map #(list 'quote %) args) 
       '~rest-args)))) 

उपयोग:

hackery> (->> (range 5) rest rest rest rest) 
(4) 
hackery> (apply-macro ->> (range 5) (repeat 4 'rest)) 
(4) 

योग्यता:

  1. मैक्रो उद्धृत नहीं किया जाना चाहिए, और बीच बहस unevaluated पारित कर रहे हैं मैक्रो के लिए।हालांकि, "आराम" तर्क मूल्यांकन किया गया है, और प्रतीकों या रूपों की सूची में मूल्यांकन करना चाहिए, जिनमें से प्रत्येक मैक्रो के लिए मूल्यांकन नहीं किया जाएगा।
  2. यह मैक्रोज़ के साथ काम नहीं करेगा जो &env तर्क का उपयोग करते हैं।
0

यह दृष्टिकोण तर्क सूची असीम लंबाई हो सकता है अगर काम नहीं करता है, लेकिन आप n arities के साथ एक आवरण समारोह बना सकते हैं n अगर आप केवल लंबाई अप करने के लिए सूची में लागू करने की आवश्यकता:

user> (defmacro foo [& rest] `(println [email protected])) 
#'user/foo 
user> (apply foo [1 2]) 
CompilerException java.lang.RuntimeException: Can't take value of a macro: #'user/foo, compiling:(*cider-repl repo*:865:7) 
user> (defn foo-up-to-ten-args 
    ([a]     (foo a)) 
    ([a b]     (foo a b)) 
    ([a b c]    (foo a b c)) 
    ([a b c d]    (foo a b c d)) 
    ([a b c d e]   (foo a b c d e)) 
    ([a b c d e f]   (foo a b c d e f)) 
    ([a b c d e f g]  (foo a b c d e f g)) 
    ([a b c d e f g h]  (foo a b c d e f g h)) 
    ([a b c d e f g h i] (foo a b c d e f g h i)) 
    ([a b c d e f g h i j] (foo a b c d e f g h i j))) 
#'user/foo-up-to-ten-args 
user> (apply foo-up-to-ten-args [1 2]) 
1 2 
nil 
user> (apply foo-up-to-ten-args (range 0 10)) 
0 1 2 3 4 5 6 7 8 9 
nil 
user> (apply foo-up-to-ten-args (range 0 11)) 
ArityException Wrong number of args (11) passed to: user/foo-up-to-ten-args clojure.lang.AFn.throwArity (AFn.java:429) 

मेरे मामले में मैंने eval के बिना जो किया था, वही किया।

0

जब एक सशर्त मैक्रो में किए गए आर्ग लागू करने or जैसे, case, cond & उदाहरण के लिए condp। आप some, every & partition फ़ंक्शंस का उपयोग कर सकते हैं। एकल बाकी खंड इन उदाहरणों में बाहर छोड़ दिया है बल्कि आसानी से

;apply to the 'or' macro 
(some identity [nil false 1 2 3]) 
=> 1 

;apply to the 'case' macro. 
(some 
    (fn [[case value]] 
    (and (= case 2) value)) 
    (partition 2 [1 "one" 2 "two" 3 "three"])) 
=> "two" 

;apply to the 'cond' macro 
(some 
    (fn [[case value]] 
    (and case value)) 
    (partition 2 [false "one" true "two" false "three" :else "four"])) 
=> "two" 

;apply to the 'condp' macro 
(let [[f v & args] [= 2 1 "one" 2 "two" 3 "three"]] 
    (some 
    (fn [[case value]] 
     (and (f case v) value)) 
    (partition 2 args))) 

every?and मैक्रो

;apply to the 'and' macro 
(every? identity [true true true]) 
=> true 
संबंधित मुद्दे