2017-06-01 62 views
1

我有一個宏defprinter,我可以在其中定義一些函數:基於解構+在打印該變量時獲取一個值。Clojure宏:只有在定義符號時才執行動作

這看起來像:

(defmacro defprinter [name pattern] 
    (list 'defn name [pattern] '(prn to-print))) 

所以我可以這樣做(defprinter print-lemons {to-print :lemons})然後(print-lemons {:lemons "lemons"})它會打印出正確的事情(我可以​​在第一個參數的任何一種解構的定義打印機)。

但現在我想給這個函數的也許知道如何與彩色打印爲好,如,如果符號color定義,它應該做(prn color "-colored " to-print),但其他人只是(prn color)的選項。

因此,由(defprinter print-lemons {to-print :lemons})生成的函數應該與上面相同,而(defprinter print-lemons {to-print :lemons, color :color})將編寫一個執行彩色版本的函數。

此外,我想要同樣的效果,如果我做 (let [color "black"] (defprinter print-lemons {to-print :lemons}))(def color "black") (defprinter print-lemons {to-print :lemons})

這可能嗎?

我試着寫了以下的方法:

(defmacro defprinter [name pattern] 
    (list 'defn name [pattern] 
    '(try (eval '(prn (str color "-colored " to-print))) 
     (catch java.lang.RuntimeException _ 
      (prn to-print))))) 

在我的理解功能的宏將寫會盡量EVAL在運行時的表情,失敗,一個RuntimeException如果color沒有定義,然後執行(prn to-print)。即使它會在運行時檢查color的存在,to-print(它始終需要存在該函數)將在宏擴展時在編譯時檢查。

但是,這裏發生的是,即使在定義了color(即使我只在eval語句中只保留to-print,它也找不到它,但catch中的子句正常工作),我總是得到一個RuntimeException。似乎這個符號沒有像我在eval期間所預期的那樣得到解決,但我想不出有任何其他方法可以實現這一點。

有什麼建議嗎?

回答

0

resolve函數可能會幫助你解決這個問題。它返回符號在當前名稱空間中表示的東西,或返回nil。您可以將它提供給if表達式:

user> (resolve 'asdf) 
nil 
user> (if (resolve 'asdf) :defined :not-defined) 
:not-defined 

請記住引用您要在測試中解析的符號。

2

首先,將您正在使用的兩個關注點分開:定義一個函數,並根據可用的變量/本地變量確定如何打印。儘可能簡化宏,可以更容易地弄清楚發生了什麼。

在決定要打印的內容,你真的有三種情況:

  1. color是本地
  2. color是VAR(或類,我猜?)
  3. color未定義

&env是一個有用的和很少使用的工具(僅宏中提供),讓你看看什麼是可以作爲一個地方。

resolve讓你看看這個名字有什麼變化。

因此,在這個例子中,有3個潛在的表達式,可以彌補身體與def-color-printer定義的功能:

(defn make-print-expression [env] 
    (if (contains? env 'color) 
    `(prn (str ~'color "-colored " ~'to-print)) 
    (if-let [color (resolve 'color)] 
     `(prn (str @~color "-colored " ~'to-print)) 
     `(prn ~'to-print)))) 

(defmacro def-color-printer [name pattern] 
    (list `defn name [pattern] 
     (make-print-expression &env))) 

(def-color-printer print-limes {to-print :limes}) 

(let [color "green"] 
    (def-color-printer print-limes-color-with-local {to-print :limes})) 

(def color "greyish") 
(def-color-printer print-limes-color-with-var {to-print :limes}) 

(print-limes {:limes "limes!"}) 
;=> "limes!" 

(print-limes-color-with-local {:limes "limes!"}) 
;=> "green-colored limes!" 

(print-limes-color-with-var {:limes "limes!"}) 
;=> "greyish-colored limes!" 

我也寫了a blog about Clojure's quoting的情況下,語法引用語法不熟悉。

相關問題