使用宏时,每次迭代后参数递增

时间:2017-12-16 11:43:06

标签: macros lisp common-lisp

所以我有这个宏(基本上是for循环):

(defmacro for ((parameter start-value end-value &optional (step 1)) &body e)
  (let ((func-name (gensym))
        (end (gensym)))
    `(labels ((,func-name (,parameter ,end)
                  (if (<= ,parameter ,end) 
                      (progn ,@e 
                             (,func-name (+ ,parameter ,step) ,end)))))
       (,func-name ,start-value ,end-value))))

我想用它来测试它:

(print (let ((j 0) (k 1))
      (for (i 1 10 (incf k)) (print i))))

我现在得到的是:

1, 3, 6, 10, NIL.

这意味着我的步骤在每次迭代后递增,但我希望它在此输出的开头只增加一次:

1, 3, 5, 7, 9, NIL.

我的宏有什么问题,我该怎么办?

1 个答案:

答案 0 :(得分:2)

您需要计算循环外的步长值:

CL-USER 12 > (defmacro for ((parameter start-value end-value
                             &optional (step 1))
                            &body e)
               (let ((func-name (gensym))
                     (step-name (gensym))
                     (end (gensym)))
                 `(labels ((,func-name (,parameter ,end ,step-name)
                             (when (<= ,parameter ,end) 
                               ,@e 
                               (,func-name (+ ,parameter ,step-name)
                                           ,end
                                           ,step-name))))
                    (,func-name ,start-value ,end-value ,step))))
FOR

CL-USER 13 > (pprint (macroexpand-1 '(for (i 1 10 (incf k)) (print i))))

(LABELS ((#:G954 (I #:G956 #:G955)
           (WHEN (<= I #:G956) (PRINT I) (#:G954 (+ I #:G955) #:G956 #:G955))))
  (#:G954 1 10 (INCF K)))

CL-USER 14 > (let ((j 0) (k 1))
               (for (i 1 10 (incf k))
                 (print i)))

1 
3 
5 
7 
9 
NIL

如果您不想一直传递步长值,则需要外部LET绑定其值。

注意:一些Lisp实现(许多解释器和一些编译器)不支持TCO(尾部调用优化)。