2012-05-13 55 views
2

我在閱讀Paul Graham's ANSI Common Lisp。在關於宏章,他顯示了以下例子:是否可以將以下lisp宏編寫爲函數?

(defmacro in (obj &rest choices) 
    (let ((insym (gensym))) 
    `(let ((,insym ,obj)) 
     (or ,@(mapcar #'(lambda (c) `(eql ,insym ,c)) 
        choices))))) 

他認爲,不能寫成函數(如果第一個參數是等於任何其他參數,則返回true) 。這個功能不會有相同的功能嗎?

(defun in (obj &rest choices) 
    (reduce (lambda (x y) 
      (or x (eql y obj))) 
      choices 
      :initial-value nil)) 

我看到的區別是宏將只評估參數,直到它找到一個eql參數。是嗎?

+5

這是相當大的。如果有人說:「我可以給你一個'IF',這不是一個宏,而只是一個功能!(但它總是評估兩個分支)」,你會說「是嗎?」 – Ashe

回答

4

問題是,如果找到匹配項,宏版本將懶惰地評估參數(它擴展爲OR)。這不能用函數實現,因爲funcall將總是首先評估所有參數。

3
> (macroexpand '(in 42 
        (long-computation-1) 
        (long-computation-2) 
        (long-computation-3))) 

(LET ((#:G799 42)) 
    (OR (EQL #:G799 (LONG-COMPUTATION-1)) 
     (EQL #:G799 (LONG-COMPUTATION-2)) 
     (EQL #:G799 (LONG-COMPUTATION-3)))) 

要得到你需要編寫同樣的效果:

(defun in (obj &rest choices) 
    (reduce (lambda (x y) 
      (or x (eql (funcall y) obj))) 
      choices 
      :initial-value nil)) 

,並使用這樣說:「難道說,它」

(in 42 
    (function long-computation-1) 
    (function long-computation-2) 
    (function long-computation-3)) 
相關問題