在DO运行之前调用在DO的结果部分中评估的EVAL

0 lisp eval common-lisp

以下代码旨在从指定的等待时间开始倒计时,然后评估提供的表单:

(defun wait (seconds form)
       (let ((end (+ (get-universal-time) seconds)))
         (do ()
          ((>= (get-universal-time) end)
           (eval form))
        (sleep 1))))
Run Code Online (Sandbox Code Playgroud)

如果我跑:

(wait 5 (format t "output"))
Run Code Online (Sandbox Code Playgroud)

结果是"输出"将在倒计时之前发送到stdout.输出"输出"后,程序仍像往常一样倒计时.

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

(defun wait (seconds form)
       (let ((end (+ (get-universal-time) seconds)))
         (do ()
          ((>= (get-universal-time) end)
           (format t "output"))
        (sleep 1))))
Run Code Online (Sandbox Code Playgroud)

为什么在DO循环中调用EVAL会在声明DO循环时进行评估,但直接插入正在评估的表单会导致它等到结果时间?

Rai*_*wig 10

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

您的函数没有获得表单(foo),而是评估结果(foo).在调用函数之前,将评估函数的所有参数.Lisp不会使用参数的形式调用函数,而是使用评估参数的结果.

你的代码

(wait                   ; function wait
  5                     ; argument expression 1
  (format t "output"))  ; argument expression 2
Run Code Online (Sandbox Code Playgroud)

怎么了?

  1. wait 是一个功能,得到它.
  2. 评估5- >5
  3. 评估(format t "output")- > NIL+输出作为副作用
  4. wait用参数5和函数调用函数NIL

改进:传递功能

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

这里发生了什么?

(wait
  5
  (lambda ()
    (format t "output")))
Run Code Online (Sandbox Code Playgroud)
  1. wait 是一个功能,得到它.
  2. 评估5- >5
  3. evaluate (lambda () (format t "output"))- >一个函数对象
  4. wait使用参数5和函数对象调用函数

现在你的函数wait需要做它想做的事情,并使用FUNCALL在正确的位置调用传递的函数对象.