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