2014-09-10 49 views
2

我是學習和使用clojure的新手,所以我有一個關於clojure中的宏的基本問題。我沒有發現你真的需要宏的情況,所以我想知道是否有真正的情況,只有宏和沒有正常的功能或multimethod解決你的問題。你在哪裏使用函數不工作的clojure中的宏

有人可以爲此顯示一個簡單的例子嗎? 我想我並不瞭解clojure中的宏的概念。

+0

這個問題可能過於廣泛(太多可能的答案)。但是,一般來說,你是對的。你不需要*(新)宏。它們對於明智的使用可能非常有用,但並非絕對必要。 – 2014-09-10 15:30:58

+0

@ A.Webb,'不是絕對必要的' - 宏是Lisp工具集的一部分,它使得這種語言如此表達。在某些層面上,需要宏在任何Lisp中編寫更好的程序,包括Clojure。 – Mark 2014-09-10 15:34:52

+0

@Mark我不認爲它們很有用,但是Clojure的用戶不需要創建一個新的宏,並且仍然會產生高度表達的代碼。如果您將宏放在工具箱的頂部架子上,將會有遺憾和返工。 – 2014-09-10 15:41:28

回答

2

Clojure宏採用文字代碼,而函數採用評估代碼。反過來,當您需要操作文字代碼時,宏很有用。除了兩個(非常重要的)實例外,文字代碼和評估代碼是等價的:符號和表達式(地圖,向量,集合,字符串,關鍵字,數字,布爾值等等都將「自己評估」)。

user=> 1 ;evaluates to itself 
1 
user=> "abc" ;evaluates to itself 
"abc" 
user=> :xyz ;evaluates to itself 
:xyz 
user=> [1 "abc" :xyz] ;evaluates to itself 
[1 "abc" :xyz] 

至於反對:

user=> (+ 1 2) ;an expression evaluates to not itself 
3 
user=> Math/PI ;a symbol evaluates to not itself 
3.141592653589793 
user=> + ;another example, a little weirder 
#<core$_PLUS_ [email protected]> 

比方說,你想創建some-fn-or-macro以這樣的表現:

user=> (some-fn-or-macro (get {:a 10 :b 20} :a)) 
"(get {:a 10 :b 20} :a)" 
user=> (some-fn-or-macro +) 
"+" 

您將無法用一個函數來做到這一點。試試看:

user=> (defn some-fn-or-macro [expr] (str expr)) 
#'user/some-fn-or-macro 
user=> (some-fn-or-macro (get {:a 10 :b 20} :a)) 
"10" 

這裏發生了什麼事?在進行字符串化之前,對some-fn-or-macro(即expr)的參數進行了評估。然而,如果我們所做的一切是從一個函數改變定義一個宏,一切都將是巨大的:

user=> (defmacro some-fn-or-macro [expr] (str expr)) 
#'user/some-fn-or-macro 
user=> (some-fn-or-macro (get {:a 10 :b 20} :a)) 
"(get {:a 10 :b 20} :a)" 

話雖這麼說,如果我們再取原函數的定義,並簡單地引用上調用的參數,這也適用:

user=> (defn some-fn-or-macro [expr] (str expr)) 
#'user/some-fn-or-macro 
user=> (some-fn-or-macro '(get {:a 10 :b 20} :a)) 
"(get {:a 10 :b 20} :a)" 

所以,你永遠只能需要如果你的用例需要的是參數仍文字/不計算寫的宏。如果你可以控制你的工具的使用方式(我猜測它總是勉強適用),你可以決定開發一個函數,並指導用戶在必要時引用參數。

***注意:我如何使用上面的宏可能會使您對宏的一個非常重要的事實置若罔聞:它們的輸出會被評估。例如:

user=> (defmacro example-macro [] '(+ 1 2)) 
#'user/example-macro 
user=> (example-macro) 
3 

您可能認爲這很奇怪。有幾種方法可以理解它。宏期望將源代碼作爲輸入,所以他們將源代碼作爲輸出是很自然的 - 源代碼需要在某個時候進行評估。實際上,我傾向於將宏和函數之間的區別看作是「移位評估」 - 評估發生在「調用之前」,參數(函數);或「在調用之後」的輸出(對於宏)。

+1

'java.lang.String'不會自己評估。輸入是一個符號,輸出是一個類。 – amalloy 2014-09-11 03:21:53

+0

優秀點。嗯...然後我會刪除整個最後的筆記 - 除非有人在第二天左右說服我。這個人爲的例子讓我覺得自己太輕浮了(實際上它已經很輕浮)。 – 2014-09-11 04:28:55

2

這裏重要的是,宏不評估它的參數,可以用於源代碼的任意轉換。宏

的最基本的例子是whenwhen-not宏:

(defmacro when 
    "Evaluates test. If logical true, evaluates body in an implicit do." 
    [test & body] 
    `(if ~test (do [email protected]))) 

(defmacro when-not 
    "Evaluates test. If logical false, evaluates body in an implicit do." 
    [test & body] 
    `(if test nil (do [email protected]))) 

功能不會在這裏工作,因爲它必須評估執行前的所有參數。

P.S.如果您對該主題感興趣並想了解更多信息,另請參閱this my answer。它是關於Common Lisp的,但它也可能對你有用。我在答案結尾還給出了一篇很酷的Paul Graham的文章。

PSS如果你想有一個新的有用的宏的例子,我想在這裏評論保羅·格雷厄姆的一句:

這將是方便在這裏,如果我能得到一個強大的一個例子宏,並說在那裏!那個怎麼樣?但是如果我這樣做了,對於不瞭解Lisp的人來說,這看起來就像是胡言亂語;這裏沒有空間來解釋你需要知道的一切,以瞭解它的含義。

+0

這是一個核心宏,而問題可能是關於新的宏。而且,這是不必要的,因爲你總是可以自己使用「if」和「do」基本特殊形式。 – 2014-09-10 15:32:27

+0

是的,他們是標準的,是的他們沒有必要。但是,爲什麼他們存在,如果他們沒有必要?也許他們會讓事情變得更容易閱讀,少一點寫作......並知道是什麼?在Lisp中存在的宏觀概念不是因爲它是必要的,而是因爲它提供了更多的權力。畢竟,如果你對電源不感興趣,那麼就有圖靈機和彙編語言。我認爲這些例子對初學者來說很簡單,容易獲得。 – Mark 2014-09-10 15:40:47