【问题标题】:Defining a macro for iterate为迭代定义宏
【发布时间】:2022-01-11 03:52:18
【问题描述】:

我想为iterate 宏定义一个新子句。类似于 Python 的range,你有一个start, stop, step。这是第一次尝试:

(defmacro-clause (for var start start stop stop step step)
  (if (minusp step)
      `(for ,var from ,start downto ,stop by (- ,step))
      `(for ,var from ,start to     ,stop by ,step)))

它使用迭代的todownto 关键字处理增加和减少范围。 (请注意,与 Python 不同,这些包括 stop 值。)

这可以根据需要工作

(iter (for x start 5 stop 3 step -1)
      (collect x))

;; => (5 4 3)

(iter (for x start 2 stop 5 step 1)
      (collect x))

;; => (3,4,5)

但是它失败了

(let ((a 9)
      (b 3)
      (c -1))
  (iter (for x start a stop b step c)
        (collect x)))

iterate 在这些地方需要明确的数字是不是有点怪?像这样的事情没有问题

(iter (for x below (+ 3 3) by (+ 1 1))
      (collect x))

具体来说,我的问题是,如何定义一个新的iterate 子句来接受在这些地方绑定到数字的变量?

【问题讨论】:

  • 您的问题是您在宏扩展时使用step。如果它不是数字文字,那将根本不起作用,否则就不会。
  • 感谢您的回复@Svante,但我不清楚您的意思。为什么您认为该步骤需要是数字文字而不是表达式?或者,换句话说,宏扩展的哪个方面使它不可接受?
  • 您正在评估宏扩展时步骤的符号。实际编译的代码只有其中一个 epansion(无论是正步骤还是负步骤)。

标签: loops macros common-lisp


【解决方案1】:

问题是您试图在宏扩展时决定当时无法知道的事情,例如变量的符号。特别是,您不能编写一个宏,该宏根据仅在运行时已知的任何内容扩展为另一个宏的(部分),或者您可以,但这必然意味着您必须在运行时调用eval 的道德等价物-时间,而且……不要那样做。

相反,您必须决定在运行时计算哪种方式。这意味着您不能使用任何(for var from ...) 或相关子句,因为似乎没有任何与方向无关的内容(为什么(for i from 1 to -5 by -1) 不起作用我无法理解,但是......好吧)。

所以我认为,无论你最终得到什么子句,都需要扩展为 (for var next ...) 子句。

这是一个尝试。免责声明:没有经过太多测试,我不使用迭代,接触可能会爆炸,对鱼有毒。

(defmacro-driver (for v in-range a to b &optional by s)
  (let ((firstp (make-symbol "FIRSTP"))
        (value (make-symbol "VALUE"))
        (limit (make-symbol "LIMIT"))
        (step (make-symbol "STEP")))
    `(progn
       (with ,firstp = t)
       (with ,value = (let ((v ,a))
                        (unless (numberp v)
                          (warn "doomed"))
                        (when (null v)
                          (warn "extremely doomed"))
                        v))
       (with ,limit = (let ((v ,b))
                        (unless (numberp v)
                          (warn "also doomed"))
                        v))
       (with ,step = (let ((v (or ,s (signum (- ,limit ,value)))))
                       (when (not (numberp v))
                         (warn "doomed again"))
                       (when (zerop v)
                         (warn "zero step"))
                       (when (not (= (signum v) (signum (- ,limit ,value))))
                         (warn "likely doomed"))
                       v))
       (,(if generate 'generate 'for)
        ,v
        next (if ,firstp
                 (progn
                   (setf ,firstp nil)
                   ,value)
               (progn
                 (incf ,value ,step)
                 (when (if (> ,step 0)
                           (>= ,value ,limit)
                         (<= ,value ,limit))
                   (terminate))
                 ,value))))))

现在

> (iter (for i in-range 1 to 5 by 2)
    (print i))

1 
3
nil

> (iter (for i in-range 1 to -1)
    (print i))

1
0
nil

> (iter (for i in-range 1 to 5 by -2)
    (when (< i -20)
      (terminate)))
Warning: likely doomed
nil

显然有些检查可能会更好。

【讨论】:

    猜你喜欢
    • 2011-10-21
    • 2011-07-09
    • 1970-01-01
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多