将clojure中的"或"操作定义为宏而不仅仅是函数有什么好处?

use*_*455 4 clojure

我希望这些解释能让我更好地了解使用宏的优势.

Cha*_*ffy 8

在函数中,所有参数在调用之前都会被计算.

这意味着,or由于函数不能是惰性的,而宏可以重写orif只在必要时才对分支求值的语句.


更具体地说,考虑一下:

(or (cached-lookup) (expensive-operation))
Run Code Online (Sandbox Code Playgroud)

......它被重写为什么样子:

(let [or__1234__auto (cached-lookup)]
  (if or__1234__auto
    or__1234__auto
    (expensive-operation)))
Run Code Online (Sandbox Code Playgroud)

......这样,我们只评估(expensive-operation)如果返回值(cached-lookup)nilfalse.在实现常规JVM调用约定时,您无法使用函数执行此操作:无论是否需要其结果,expensive-operation都将始终对其进行求值,以便将其结果作为参数传递给函数.


顺便提一下,如果将零参数函数作为参数,则可以在这种情况下实现函数.也就是说,你可以这样做:

(defn or*
  ([] false)                                                ; 0-arg case
  ([func-one] (func-one))                                   ; 1-arg case
  ([func-one func-two]                                      ; optimized two-arg case
   (let [first-result (func-one)]
     (if first-result
       first-result
       (func-two))))
  ([func-one func-two & rest]                               ; general case
   (let [first-result (func-one)]
     (if first-result
       first-result
       (apply or* func-two rest)))))
Run Code Online (Sandbox Code Playgroud)

当你必须实现一个宏时,让它生成"thunks"(匿名函数)并将它们传递给更高阶的函数(例如这个函数)通常会很有帮助.这基本上有助于可组合性,因为可以使用更高级别的函数来包装,修改或调用函数,例如partial宏不能的方式.