[英]Trouble understanding a simple macro in Clojure - passing macro as stand-in for higher order function to map
我最近一直在進入Clojure並且直到現在才避免使用宏,所以這是我第一次接觸它們。 我一直在閱讀“掌握Clojure宏”,在第3章,第28頁,我遇到了以下示例:
user=> (defmacro square [x] `(* ~x ~x))
;=> #'user/square
user=> (map (fn [n] (square n)) (range 10))
;=> (0 1 4 9 16 25 36 49 64 81)
上下文是作者解釋說,雖然簡單地傳遞square
宏來映射導致錯誤(不能取宏的值),但將它包裝在函數中是有效的,因為:
當匿名函數(fn [n](square n))被編譯時,方形表達式被宏擴展到(fn [n](clojure.core / * nn))。 這是一個非常合理的函數,因此編譯器沒有任何問題
如果我們假設在運行時(在編譯或“定義”時間)評估函數體 ,從而在運行時之前擴展宏,這對我來說是有意義的。 但是,我始終認為, 人體的功能不評價,直到運行時,在編譯時,你會基本上只是有一個函數對象與它的一些知識的詞匯范圍,(但沒有它的身體的知識)。
我在這里明顯地混淆了編譯/運行時語義,但是當我看到這個示例時,我一直認為在map強制調用之前不會擴展square,因為它位於匿名函數的主體中,我認為直到運行時才會被評估。 我知道我的想法是錯誤的,因為如果是這樣的話,那么n
將被綁定到(range 10)
每個數字,並且不存在問題。
我知道這是一個非常基本的問題,但事實證明,在第一次曝光時我完全包裹我的腦子非常棘手!
一般來說,在編譯時不會對函數體進行求值 ,但是宏總是在編譯時進行求值,因為它們總是在編譯時擴展,無論是否在函數內部。
您可以編寫一個擴展為函數的宏,但您仍然無法引用/傳遞宏,就好像它是一個函數:
(defmacro inc-macro [] `(fn [x#] (inc x#)))
=> #'user/inc-macro
(map (inc-macro) [1 2 3])
=> (2 3 4)
defmacro
在編譯時擴展,因此您可以將其視為編譯期間執行的函數。 這將用“返回”代碼替換每次出現的宏“調用”。
您可以將宏視為語法規則。 例如,在Scheme中,使用define-syntax形式聲明宏。 編譯器中有一個特殊步驟,在編譯代碼之前將所有宏調用替換為其內容。 因此,您的最終代碼中不會有任何square
調用。 說,如果你寫了類似的東西
(def value (square 3))
擴展后的最終版本將是
(def value (clojure.core/* 3 3))
有一種特殊的方法可以在擴展后檢查宏的主體:
user=> (defmacro square [x] `(* ~x ~x))
#'user/square
user=> (macroexpand '(square 3))
(clojure.core/* 3 3)
這就是為什么宏是一個短暫的東西,它只存在於源代碼中,而不是它的編譯版本。 這就是為什么它不能作為值傳遞或以某種方式引用。
關於宏的最佳規則是:在你的工作中真正需要它們之前盡量避免使用它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.