2016-01-24 3 views
0

Следующий код предназначен для отсчета времени от заданного времени ожидания, а затем оценить прилагаемую форму:Призыва к EVAL оценивается в результате части DO до DO работает

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

Почему бы вызов EVAL в цикле DO быть оценки, когда цикл DO объявлен, но непосредственная вставка оцениваемой формы заставляет его ждать до момента результата?

ответ

10

Первый закон программирования Лиспа для начинающих: нет, вам не нужно eval.

Ваша функция не принимает форму (foo), но результат оценки (foo). Все аргументы для функций оцениваются ПЕРЕД вызовом функции. Lisp не вызывает функцию с формами аргументов, а с результатами оценки аргументов.

Ваш код

(wait     ; function wait 
    5      ; argument expression 1 
    (format t "output")) ; argument expression 2 

Что происходит?

  1. wait - это функция, получить ее.
  2. оценка 5 ->5
  3. оценивать (format t "output") ->NIL + выхода в качестве побочного эффекта
  4. вызова функции wait с аргументами 5 и NIL

Улучшения: передать функцию

Если вы не хотите запускать аргумент в вызове, создайте удовольствие ction (lambda() (foo)), который будет оцениваться функциональным объектом, передать его переменной delayed-function и назовите ее (funcall delayed-function).

Что здесь происходит?

(wait 
    5 
    (lambda() 
    (format t "output"))) 
  1. wait является функцией, получить его.
  2. оценки 5 ->5
  3. оценки (lambda() (format t "output")) -> объект функции
  4. вызов функции wait с аргументами 5 и функции объекта

Теперь ваша функция wait должна делать то, что он хочет и вызовите переданный объект функции в нужном месте - с помощью FUNCALL.

2

Когда вы вызываете функцию, ее аргументы вычисляются один раз до того, как они передаются функции. Вы можете использовать макрос, если хотите передать неоцененные формы. Например .:

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