Macros 当看到0时停止计算的lisp宏乘法

Macros 当看到0时停止计算的lisp宏乘法,macros,lisp,Macros,Lisp,我对LISP非常陌生,我不知道如何使我的无限参数函数在其中一个因子为0时停止计算其余参数。我试过这个 (defmacro smart_multiplication (&rest l) (unless(member 0 l) `(* ,@l)) ) 但我认为这并不会停止乘法,直到它检查所有变量。一个,除非表单在谓词未命中时计算为nil: (macroexpand-1 '(smart-multiplication 1 2 0)) ; ==> nil 也许您应该使用ìf,

我对LISP非常陌生,我不知道如何使我的无限参数函数在其中一个因子为0时停止计算其余参数。我试过这个

(defmacro smart_multiplication (&rest l)
  (unless(member 0 l) `(* ,@l))
)

但我认为这并不会停止乘法,直到它检查所有变量。

一个
,除非
表单在谓词未命中时计算为
nil

(macroexpand-1 '(smart-multiplication 1 2 0))    
; ==> nil
也许您应该使用
ìf
,因为您更希望它变成零

(defmacro smart-multiplication (&rest l)
  (if (member 0 l)
      0
      `(* ,@l)))

(macroexpand-1 '(smart-multiplication 1 2 3 0)) 
; ==> 0
(macroexpand-1 '(smart-multiplication 1 2 3)) 
; ==> (* 1 2 3)
请注意,它仅适用于文字零,而且由于宏只知道变量是符号,而从不知道它们的值,因此当您有零变量时,它将不起作用:

(defparameter *zero* 0)
(macroexpand-1 '(smart-multiplication 1 2 3 *zero*)) 
; ==> (* 1 2 3 *zero*)
基本上,您不能将宏视为程序计算,因为它只抽象代码。从而使语法被改写成公共Lisp支持的东西。例如,
除非是宏:

(macroexpand-1 '(unless some-predicate
                  result-1
                  result-2))
; ==> (if (not some-predicate) (progn result-1 result-2))

您需要在运行时检查该值是否为零

(defmacro smart-multiplication (&rest list)
  (if (null list)
      1
    (let ((n0sym (gensym "mult")))
      `(let ((,n0sym ,(first list)))
         (if (zerop ,n0sym)
              0
            (* ,n0sym (smart-multiplication ,@(rest list))))))))

CL-USER 42 > (smart-multiplication (print 1) (print 2) (print 3))

1 
2 
3 
6

CL-USER 43 > (smart-multiplication (print 1) (print 0) (print 3))

1 
0 
0
生成的代码使用Lispworks code walker完全展开:

问题

现在代码看起来仍然不是很好。 改进留作练习:

  • 块的返回可用于返回0,而无需执行open
    *
    函数调用
  • 更好的是:不要使用嵌套的乘法调用
提示一个简单的解决方案:生成的代码可能类似于以下代码:

(prog ((result 1)
       value)

  (setf value (print 1))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (setf value (print 0))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (setf value (print 3))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (return result))

&rest
参数不是无限长的。它们可以与变量
调用参数limit
的值一样短,该值可以低至50。因此,在可移植代码中,不能假设函数允许超过50个数字作为参数。
(prog ((result 1)
       value)

  (setf value (print 1))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (setf value (print 0))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (setf value (print 3))
  (when (zerop value)
    (return 0))
  (setf result (* result value))

  (return result))