Common Lisp中LABELS内部未定义的函数错误

时间:2016-12-20 07:36:30

标签: common-lisp

我有:

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

不应该处理程序可用,因为它在表单上以词汇方式关闭了吗?

2 个答案:

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