Lisp 在DO运行之前在DO的结果部分中对EVAL进行求值的调用

Lisp 在DO运行之前在DO的结果部分中对EVAL进行求值的调用,lisp,common-lisp,eval,Lisp,Common Lisp,Eval,以下代码用于从指定的等待时间开始倒计时,然后计算提供的表单: (defun wait (seconds form) (let ((end (+ (get-universal-time) seconds))) (do () ((>= (get-universal-time) end) (eval form)) (sleep 1)))) 如果我跑步: (wait 5 (format t "output"

以下代码用于从指定的等待时间开始倒计时,然后计算提供的表单:

(defun wait (seconds form)
       (let ((end (+ (get-universal-time) seconds)))
         (do ()
          ((>= (get-universal-time) end)
           (eval form))
        (sleep 1))))
如果我跑步:

(wait 5 (format t "output"))
结果是输出将在倒计时之前发送到标准输出。输出后,程序仍像往常一样倒计时

我得到了预期的结果,其中输出在倒计时完成后发送到stdout,代码如下:

(defun wait (seconds form)
       (let ((end (+ (get-universal-time) seconds)))
         (do ()
          ((>= (get-universal-time) end)
           (format t "output"))
        (sleep 1))))
为什么在声明DO循环时会对DO循环中的EVAL调用进行求值,但直接插入正在求值的表单会导致它等待结果时间?

初学者Lisp编程的第一定律:不,不需要EVAL

您的函数不会得到表单foo,而是计算foo的结果。函数的所有参数都在调用函数之前求值。Lisp不使用参数的形式调用函数,而是使用参数求值的结果

你的代码

会发生什么

等待是一个函数,获取它。 评估5->5 评估格式t输出->零+输出作为副作用 使用参数5和NIL调用函数wait 改进:传递一个函数

如果不想在调用中运行参数,请创建一个函数lambda foo,该函数将被计算为函数对象,并将其传递给变量delayed函数,然后使用funcall delayed函数调用它

这里发生了什么

(wait
  5
  (lambda ()
    (format t "output")))
等待是一个函数,获取它。 评估5->5 计算lambda格式t输出->函数对象 使用参数5和函数对象调用函数wait
现在,您的函数wait需要做它想做的事情,并在正确的位置调用传递的函数对象-使用。

当您调用一个函数时,它的参数在传递给函数之前被求值一次。如果要传递未计算的表单,可以使用宏。例如:

(defmacro wait (seconds form)
  (let ((end-name (gensym "end")))
    `(do ((,end-name (+ (get-universal-time) ,seconds)))
         ((>= (get-universal-time) ,end-name))
       ,form
       (sleep 1))))
请参见其宏扩展:

CL-USER> (macroexpand-1 '(wait 10 (print 'test)))
(DO ((#:|end868| (+ (GET-UNIVERSAL-TIME) 10)))
    ((>= (GET-UNIVERSAL-TIME) #:|end868|))
  (PRINT 'TEST)
  (SLEEP 1))
CL-USER> (macroexpand-1 '(wait 10 (print 'test)))
(DO ((#:|end868| (+ (GET-UNIVERSAL-TIME) 10)))
    ((>= (GET-UNIVERSAL-TIME) #:|end868|))
  (PRINT 'TEST)
  (SLEEP 1))