在 DO 运行之前调用 EVAL 在 DO 的结果部分进行评估
Call to EVAL being evaluated in result portion of DO before DO runs
以下代码旨在从指定的等待时间开始倒计时,然后评估提供的表格:
(defun wait (seconds form)
(let ((end (+ (get-universal-time) seconds)))
(do ()
((>= (get-universal-time) end)
(eval form))
(sleep 1))))
如果我运行:
(wait 5 (format t "output"))
结果是 "output" 会在倒计时之前发送到 stdout。输出"output"后程序还是照常倒计时
我得到了预期的结果,其中 "output" 在倒计时完成后发送到标准输出,代码如下:
(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 不使用参数的形式调用函数,而是使用评估参数的结果。
您的代码
(wait ; function wait
5 ; argument expression 1
(format t "output")) ; argument expression 2
会发生什么?
wait
是一个函数,搞定。
- 评估
5
-> 5
- 评估
(format t "output")
-> NIL
+ 作为副作用输出
- 使用参数
5
和 NIL
调用函数 wait
改进:传递一个函数
如果您不想 运行 调用中的参数,请创建一个函数 (lambda () (foo))
,它将被计算为一个函数对象,将其传递给一个变量 delayed-function
, 并用 (funcall delayed-function)
.
调用它
这里发生了什么?
(wait
5
(lambda ()
(format t "output")))
wait
是一个函数,搞定。
- 评估
5
-> 5
- 评估
(lambda () (format t "output"))
-> 函数对象
- 使用参数
5
和函数对象 调用函数 wait
现在你的函数 wait
需要做它想做的事并在正确的地方调用传递的函数对象 - 使用 FUNCALL.
当你调用一个函数时,它的参数在传递给函数之前会被评估一次。如果你想传递未计算的表单,你可以使用宏。例如:
(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))
以下代码旨在从指定的等待时间开始倒计时,然后评估提供的表格:
(defun wait (seconds form)
(let ((end (+ (get-universal-time) seconds)))
(do ()
((>= (get-universal-time) end)
(eval form))
(sleep 1))))
如果我运行:
(wait 5 (format t "output"))
结果是 "output" 会在倒计时之前发送到 stdout。输出"output"后程序还是照常倒计时
我得到了预期的结果,其中 "output" 在倒计时完成后发送到标准输出,代码如下:
(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 不使用参数的形式调用函数,而是使用评估参数的结果。
您的代码
(wait ; function wait
5 ; argument expression 1
(format t "output")) ; argument expression 2
会发生什么?
wait
是一个函数,搞定。- 评估
5
->5
- 评估
(format t "output")
->NIL
+ 作为副作用输出 - 使用参数
5
和NIL
调用函数
wait
改进:传递一个函数
如果您不想 运行 调用中的参数,请创建一个函数 (lambda () (foo))
,它将被计算为一个函数对象,将其传递给一个变量 delayed-function
, 并用 (funcall delayed-function)
.
这里发生了什么?
(wait
5
(lambda ()
(format t "output")))
wait
是一个函数,搞定。- 评估
5
->5
- 评估
(lambda () (format t "output"))
-> 函数对象 - 使用参数
5
和函数对象 调用函数
wait
现在你的函数 wait
需要做它想做的事并在正确的地方调用传递的函数对象 - 使用 FUNCALL.
当你调用一个函数时,它的参数在传递给函数之前会被评估一次。如果你想传递未计算的表单,你可以使用宏。例如:
(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))