簡體   English   中英

無法理解Clojure中的一個簡單宏 - 將宏作為替代函數傳遞給高階函數進行映射

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM