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

时间:2017-03-09 23:57:40

标签: macros lisp

我对LISP很陌生,我不知道当其中一个因子为0时,如何使我的无限参数函数停止评估其余参数。我试过这个

foo = c("Animal", foo)
df = data.frame(do.call(rbind, split(foo, ceiling(seq_along(foo)/4))),
                                                      stringsAsFactors = FALSE)
colnames(df) = df[1,]
df = df[-1,]
df
#  Animal A B C
#2    Dog 1 2 3
#3    Cat 4 5 6
#4   Goat 7 8 9

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

2 个答案:

答案 0 :(得分:0)

当谓词没有达到时,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))

答案 1 :(得分:0)

您需要在运行时检查零值。

(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 代码漫游器完全展开生成的代码:

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