我正在尝试编写一个时间线生成器,它采用包含要执行的时间和函数的 plist:
(defun run-timeline (timeline)
"Timeline is a plist with alternating timecodes and code to execute"
(alexandria:doplist (time form timeline)
(sb-ext:schedule-timer
(sb-ext:make-timer form
:thread t)
time)))
(run-timeline '(0 (print "first")
2 (print "second")))
然而,这给了我这个错误:
The value
(PRINT "first")
is not of type
(OR FUNCTION SYMBOL)
[Condition of type TYPE-ERROR]
Restarts:
0: [USE-VALUE] Use specified value.
1: [ABORT] abort thread (#<THREAD tid=203285 "Timer NIL" RUNNING {1001179523}>)
Backtrace:
0: (SB-VM::CALL-SYMBOL)
1: ((FLET SB-THREAD::WITH-RECURSIVE-LOCK-THUNK :IN SB-IMPL::MAKE-CANCELLABLE-INTERRUPTOR))
2: ((FLET "WITHOUT-INTERRUPTS-BODY-" :IN SB-THREAD::CALL-WITH-RECURSIVE-LOCK))
3: ((FLET "WITHOUT-INTERRUPTS-BODY-1" :IN SB-IMPL::MAKE-CANCELLABLE-INTERRUPTOR))
4: ((LAMBDA NIL :IN SB-IMPL::MAKE-CANCELLABLE-INTERRUPTOR))
5: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
6: ((FLET "WITHOUT-INTERRUPTS-BODY-" :IN SB-THREAD::RUN))
7: ((FLET SB-UNIX::BODY :IN SB-THREAD::RUN))
8: ((FLET "WITHOUT-INTERRUPTS-BODY-" :IN SB-THREAD::RUN))
9: (SB-THREAD::RUN)
10: ("foreign function: call_into_lisp_")
当对 lambda 表达式进行硬编码时,我没有收到任何错误,并且它按预期工作:
(defun run-timeline (timeline)
"Timeline is a plist with alternating timecodes and code to execute"
(alexandria:doplist (time form timeline)
(sb-ext:schedule-timer
(sb-ext:make-timer (lambda ()
(print "hi")
(force-output))
:thread t)
time)))
我在这里遗漏了什么?
函数
sb-ext:make-timer
需要一个函数指示符,它可以是一个符号(参见FBOUNDP
)或对某个表单求值的结果(function ...)
(参见)。例如,FUNCTION
符号是 的快捷方式, 指的是当前与符号 关联的函数值。同样,是 的快捷方式, 求值为匿名函数(或闭包)。#'xyz
(function xyz)
xyz
(lambda () (print :ok))
(function (lambda () (print :ok)))
在您的例子中,
make-timer
给定一个列表,即(print "first")
。您必须确保将该列表转换为函数,否则您可能需要更改属性列表所包含的值。这个更简单,例如,您可以使用以下命令调用当前函数:请注意,我删除了引号字符,因为您需要评估形式才能发挥功能。
或者,您可以保留此语法:
但是您必须更改实现,以便可以将任何形式转换为函数。例如,您可以编写以下内容来生成可以评估为函数的形式:
逗号将 form 的当前值(例如
(print "first")
)注入到周围的准引用形式中,以获取列表(lambda () (print "first"))
。 周围的调用将eval
其转换为实际的函数对象(例如,通过编译它,但这不是必需的)。还有另一种将 lambda 形式转换为函数的方法,如下所示(参见
COERCE
):