【问题标题】:lisp macro multiplication that stops evaluating when sees a 0当看到 0 时停止评估的 lisp 宏乘法
【发布时间】:2017-03-09 23:57:39
【问题描述】:

我对 LISP 很陌生,我不知道如何让我的无限参数函数在其中一个因素为 0 时停止评估其余参数。我已经尝试过了

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

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

【问题讨论】:

  • &rest 参数不是无限长的。它们可以像变量call-arguments-limit 的值一样短,可以低至 50。因此,在可移植代码中,您不能假设该函数允许超过 50 个数字作为参数。

标签: macros lisp


【解决方案1】:

当谓词未命中时,unless 表单的计算结果为 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*)

基本上,您不能将宏视为程序计算,因为它只是抽象代码。从而使语法被重写为 Common Lisp 支持的东西。例如。 unless 是一个宏:

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

【讨论】:

    【解决方案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 完全扩展生成的代码:

    CL-USER 46 > (pprint (walker:walk-form
                          '(smart-multiplication (print 1)
                                                 (print 0)
                                                 (print 3))))
    
    (LET ((#:|mult973| (PRINT 1)))
      (IF (ZEROP #:|mult973|)
          0
        (* #:|mult973|
           (LET ((#:|mult974| (PRINT 0)))
             (IF (ZEROP #:|mult974|)
                 0
               (* #:|mult974|
                  (LET ((#:|mult975| (PRINT 3)))
                    (IF (ZEROP #:|mult975|) 0 (* #:|mult975| 1)))))))))
    

    问题

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

    • 块的返回可用于返回 0,而无需执行打开的 * 函数调用。
    • 更好:不要使用嵌套乘法调用...

    提示一个简单的解决方案:生成的代码可能与此类似:

    (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))
    

    【讨论】:

      猜你喜欢
      • 2012-01-09
      • 1970-01-01
      • 1970-01-01
      • 2018-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-08
      • 1970-01-01
      相关资源
      最近更新 更多