我有:
(defun serve (&key (port 80) (handler #'IDENTITY))
(WITH-OPEN-SOCKET (socket :LOCAL-PORT port :LOCAL-HOST "localhost" :CONNECT :PASSIVE :REUSE-ADDRESS t)
(LABELS
((next-connection ()
(with-open-stream (stream (ACCEPT-CONNECTION socket :wait t))
(handler stream))
(next-connection)))
(next-connection))))
在输入REPL(Clozure Common Lisp)时生成警告:
;Compiler warnings :
; In NEXT-CONNECTION inside SERVE: Undefined function HANDLER
; In SERVE: Unused lexical variable HANDLER
为什么处理程序变量不可用?
这不只是一个警告,运行它确认错误:
(serve :port 6663 :handler #'do-stuff)
输出:
Error: Undefined function HANDLER called with arguments (#<BASIC-TCP-STREAM ...>)
不应该处理程序可用,因为它在表单上以词汇方式关闭了吗?
答案 0 :(得分:4)
我把你的问题减少到了这个:
(defun serve (handler)
(handler 'stream))
表明它与labels
(或with-XXX
宏无关)。
实际问题是,当Lisp评估列表时,它会查看列表中的第一项,期望它是一个符号,查找符号的 function 属性,并调用功能。 (如果您不理解&#34;属性&#34;或&#34;符号的单元格,请告诉我们,我们将对此进行更多扩展。)
如果使用handler
定义了defun
,但是因为它是一个&#34;局部变量&#34;在serve
内,您希望Lisp执行其 value 属性引用的函数。
为此,您使用Lisp的funcall
功能。最小的例子现在变成:
(defun serve (handler)
(funcall handler 'stream))
(funcall handler stream)
答案 1 :(得分:3)
您可能希望将代码格式化得更具可读性。我就是这样做的,使用编辑器进行缩进:
(defun serve (&key (port 80) (handler #'IDENTITY))
(WITH-OPEN-SOCKET (socket
:LOCAL-PORT port
:LOCAL-HOST "localhost"
:CONNECT :PASSIVE
:REUSE-ADDRESS t)
(LABELS ((next-connection ()
(with-open-stream (stream (ACCEPT-CONNECTION socket :wait t))
(funcall handler stream))
(next-connection)))
(next-connection))))
请注意,您编写的循环就像在Scheme中一样。 在Common Lisp中
(LABELS ((next-connection ()
(with-open-stream (stream (ACCEPT-CONNECTION socket :wait t))
(funcall handler stream))
(next-connection)))
(next-connection)
只是
(loop
(with-open-stream (stream (ACCEPT-CONNECTION socket :wait t))
(funcall handler stream)))
优点:
你也可以使连接代码成为一个函数,并且仍然使用一个简单的循环:
(flet ((handle-connection ()
(with-open-stream (stream (ACCEPT-CONNECTION socket :wait t))
(funcall handler stream))))
(loop (handle-connection)))