【问题标题】:Why is #' (sharp-quote) notation unnecessary in CLISP?为什么在 CLISP 中不需要#'(尖引号)符号?
【发布时间】:2014-01-28 15:46:11
【问题描述】:

我正在从“实用通用 Lisp”一书中学习 Lisp。在某一时刻,我应该输入以下代码:

[1] (remove-if-not #'evenp '(1 2 3 4 5 6 7 8 9 10))
(2 4 6 8 10)

我想这里的想法当然是 remove-if-not 想要一个在提供参数时可以返回 T 或 NIL 的函数,然后将此函数应用于列表中的所有符号,返回一个仅包含返回 NIL 的那些符号的列表。

但是,如果我现在在 CLISP 中编写以下代码:

[2] (remove-if-not 'evenp '(1 2 3 4 5 6 7 8 9 10)
(2 4 6 8 10)

它仍然有效!所以我的问题是,我是否使用尖引号,或者仅仅使用引号就足够了?现在看来,附加的尖角只是为了让程序员知道“嘿,这是一个函数,而不仅仅是一些随机符号!” - 但如果它有任何其他用途,我很想知道。

我使用 GNU CLISP 2.49(2010-07-07,哎呀,这实际上已经很老了)。

【问题讨论】:

标签: lisp common-lisp clisp


【解决方案1】:

Sharp-quote 和 quote 在一般情况下没有相同的行为:

(defun test () 'red)

(flet ((test () 'green))
  (list (funcall 'test)
        (funcall #'test))) => (red green)

调用带引号的符号将使用带引号的符号的函数值(即symbol-function 的结果)。调用一个带引号的符号将使用该符号的词法绑定(如果有的话)建立的值。在公认的常见情况下,没有词法绑定,行为将是相同的。这就是你所看到的。

你应该养成使用尖引号的习惯。忽略函数绑定可能不是您想要的,并且可能会让任何试图理解您的代码的人感到困惑。

【讨论】:

  • 在某些情况下您需要函数的名称而不是函数,例如(defvar *some-fn* 'my-fn)。现在,您可以在不更改变量的情况下重新定义 my-fn,就像使用 #'my-fn 一样。
  • 通过符号调用函数不会让任何真正的 Lisp 程序员感到困惑。当你想引用一个符号的全局函数绑定时它很有用,即使有一个词法绑定,比如 C++ 中的::foo()。它还确保后期绑定:在评估调用时解决绑定。而#'func 可以以这样一种方式编译:当随后重新定义func 时(调用者和被调用者在同一个文件中),调用仍会转到func 的先前定义。
【解决方案2】:

这不是 CLISP 特定的,它适用于每个 Common Lisp 实现(我在这里使用 Clozure Common Lisp)。

如果您将符号指定为 function designator,那么实现会为您查找 symbol-function(假设该符号在全局环境中可用):

? #'evenp
#<Compiled-function EVENP #x3000000F2D4F>
? (symbol-function 'evenp)
#<Compiled-function EVENP #x3000000F2D4F>

一般来说,你可以使用任何一种,但是如果你稍后重新绑定被调用的函数,会有一个有趣的效果。如果您指定函数(#'(function)),则调用仍将调用旧函数,因为查找已在编译时完成;如果您使用该符号,那么您将调用新函数,因为查找是在运行时重新完成的。请注意,这可能是特定于实现的。

【讨论】:

  • 你真的试过你说的吗?取CLISP...使用函数quote也可以取到改变的函数。
  • 值得注意的是,#'(lambda ...)(lambda ...) 有效,但 '(lambda ...) 无效。
  • @RainerJoswig 是的,但在 CLISP 中没有。这可能是特定于实现的,但知道它仍然很有用。
  • 如果您使用文件编译,可能会出现这种情况。但是在使用解释器和增量编译器的交互式计算中,Lisp 通常会选择当前定义。
【解决方案3】:

正如您已经注意到(或阅读)的 funcall 等。人。 are 将努力将您提供的函数参数转换为适当的参数。因此,正如您所注意到的,它们将获取一个符号,然后获取该符号的符号函数;如果成功了,他们就会调用它。

回想一下,#'X 在读取时被转换为 (symbol-function x) 和 'x 到 (quote x)。最好在编译时完成符号函数的工作。

但是为什么呢?好吧,有两个微不足道的原因,它稍微快了一点,它表明您不打算在编译后重新定义 F 的符号函数。另一个原因是,在最近的一项 Pew Research 研究中,98.3% 的 Lisp 开发人员更喜欢它,而 62.3% 的人会避开那些不这样做的人。

但还有更多。

'(lambda (..) ...) 与 v.s. 完全不同。 #'(lambda (..) ...)。第一个很可能最终使用 eval,即它会很慢。第一个在不同的范围内运行 vs.第二个,即只有第二个可以看到它出现的词法范围。

【讨论】:

  • 最后一段有点误导,偏离了答案。语法 (lambda ...) 本身不是 Common Lisp 中的函数指示符。 (funcall '(lambda ...) ...) 是错误的。可以,不过像这样(funcall (coerce '(lambda () 42) 'function)).
猜你喜欢
  • 1970-01-01
  • 2012-05-18
  • 2014-08-09
  • 1970-01-01
  • 2019-08-13
  • 1970-01-01
  • 2012-03-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多