3

在阅读其他开发人员编写的 Commom Lisp 代码时,我注意到他们中的一些人调用VALUES访问器,而没有在返回值是无效或可忽略的定义的最后一种形式中提供任何参数。

例子:

(defun definition-whose-return-values-are-neglectable ()
  ... Some s-expressions ...
  (values))

将此形式添加为这些类型定义中的最后一个表达式是否有任何优势(对于编译器、性能等)?

4

3 回答 3

3

一件有用的事情是,当您在 REPL 中调用该函数时,您最终不会打印出随机值。这在与系统交互时非常方便。

于 2013-05-29T09:10:16.950 回答
3

印刷

Lisp中没有invisible,所以无论你的函数返回什么,都将由PREPL 的 (print) 部分打印。这意味着如果您的打印设置不合适,系统可能会打印很多屏幕(您甚至可能会得到一个令人困惑的堆栈溢出错误,因为您的print-circlenil并且返回值是一个循环结构)。

汇编

编译器可以做一些优化(具体的编译器在实践中可能会或可能不会做);例如,它可能会将函数标记为不返回任何有趣的内容,然后编译表达式,例如(setq var (my-func))to (progn (my-func) (setq var nil))

如果编译器可以证明该函数没有副作用并且您以它结束,(values)那么编译器可以完全放弃它的调用。

文档

计算机编程的一个重要方面是您的代码将被包括您自己在内的其他人阅读,并且添加(values)到函数的末尾告诉读者该函数仅用于副作用。

于 2013-05-29T13:40:59.643 回答
3

最后一种形式的优点或主要用例(values)是:

  • 当返回值对定义无意义时的自记录代码
  • 通常,避免开发工具处理或保留对被忽视或不必要的结果的引用;
    具体来说:
    • 为避免在 REPL 中打印长序列或无限序列,如果您已 *print-circle*设置为t*print-length*设置为nil*print-level*设置为nil或任何类似的实现特定参数
    • REPLs 设置*nil无值被视为 的结果 nil,并设置/为空列表
    • 在 Allegro CL 的 IDE 中,ListenersTrace 对话框 保持对结果的引用,直到它们被清除1
    • 在 LispWorks 的 IDE 中,Tracer工具会在输出数据视图中保留对结果的引用,直到它们被清除
    • 在 SLIME 中,REPL 在演示文稿中保留对结果的引用, 直到它们被清除

非优势/非问题:

  • 一些编译器生成的指令少一些,有些生成的指令多一些,但除非你正在处理字节和/或周期,否则这可以忽略不计
  • 您已经不需要返回任何值,例如:
    • 当您期望调用者区分返回值的数量时
      这可能也暴露了定义的错误设计
    • 当结果应该在 multiple-value-call表单中使用时
      这可能还暴露了调用者的不良设计,这实际上在表单之间产生了副作用
      除了消除副作用或允许副作用发生之外,这里几乎没有什么可做的在整个之前或之后multiple-value-call

问题:

  • 通常,它会从定义中禁用尾调用优化。
    然而,一个足够聪明的编译器可能会检测到对无值定义的尾调用,通常在s/ s 的帮助下,并在这些调用中应用优化 这并非没有风险其他定义正在更改,所以我的猜测是没有实现ftype declaredeclaim

1.您可以通过将历史大纲的内容设置为空列表来清除 Allegro CL 中的侦听器历史:

(setf (cg:range (cg:find-component
                 :history-outline
                 (first (cg:toolbars ide.base:*listener-window*))))
      '())
于 2013-05-29T18:12:57.183 回答