阅读书"让Over Lambda"作者:Doug Hoyte,我发现了#.
符号的以下描述,a.k.a. read-macro:
使用COMMON LISP内置的基本读取宏是#。读取时间eval宏。此读取宏允许您将对象嵌入到您无法序列化的表单中,但可以使用一些lisp代码创建。
从第4章开始,本书的大部分内容可以在这里找到: http://letoverlambda.com/index.cl/toc
这是本书中的示例,显示了每次都可以不同地读取相同的表达式:
* '(football-game
(game-started-at
#.(get-internal-real-time))
(coin-flip
#.(if (zerop (random 2)) 'heads 'tails)))
(FOOTBALL-GAME
(GAME-STARTED-AT 187)
(COIN-FLIP HEADS))
* '(football-game
(game-started-at
#.(get-internal-real-time))
(coin-flip
#.(if (zerop (random 2)) 'heads 'tails)))
(FOOTBALL-GAME
(GAME-STARTED-AT 309)
(COIN-FLIP TAILS))
接下来,作者演示了一些硬核技巧,使用#
宏创建变体。
因此,事实证明#'
也是某种读取宏,它通常在表示函数名称的符号之前使用。但这是否必要,以及那里的工作究竟是什么?
我可以使用#'
或不使用它来为高阶函数添加符号:
CL-USER> (defun test nil t)
TEST
CL-USER> (funcall #'test)
T
CL-USER> (funcall 'test)
T
取得了同样的成功。
答案 0 :(得分:7)
您可以双向调用函数的全局定义:
CL-USER> (defun test nil t)
TEST
CL-USER> (funcall #'test)
T
CL-USER> (funcall 'test)
T
但请看:
CL-USER 10 > (defun foo () 42)
FOO
CL-USER 11 > (flet ((foo () 82))
(print (foo))
(print (funcall 'foo))
(print (funcall #'foo)))
82 ; the local definition
42 ; the symbol references the global definition
82 ; (function foo) / #'foo references the local definition
(funcall 'foo)
从符号中查找函数。
(funcall #'foo)
从词法环境调用函数。如果没有,则使用全局定义。
#'foo
是(function foo)
的简写符号。
CL-USER 19 > '#'foo
(FUNCTION FOO)
答案 1 :(得分:7)
与大多数语言不同,Common Lisp并不真正拥有解析器。它有一个被称为读者的词法分析器。阅读器使用单个字符并在表格中查找它们并调用那里找到的函数[1]。在Lisp中,解析器在其他语言中扮演的角色由宏实现。
[1] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_b.htm
例如,分号的阅读器会占用该行的其余部分,并将其作为注释丢弃。所以,例如,读者开放paren。调用递归读取列表元素的函数。因此,例如单引号递归读取单个表单然后将其包装在引号中。因此'(1 2 3)被读作(引用(1 2 3))。这些关键的复杂令牌读者有很多。
[2] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_d.htm
角色#\#
提供了一个放置一系列额外读者行为的地方。哈希读者重复主读者的设计。它消耗另一个角色并在表格中查找并调用在那里找到的函数。其中有很多[3]。
[3] http://www.lispworks.com/documentation/lw51/CLHS/Body/02_dh.htm
因此,例如,我们有一个类似于读取矢量的列表的读取器,例如, #(1 2 3)。因此,例如,我们有一个单个字符的阅读器,您可以分别输入单个分号,双引号或句点#\;
,#\"
和#\.
。
回答您的具体问题:报价的哈希读者,例如: #' foo,类似于常规报价的那个。它读取以下标记并将其包装在函数中。 #' foo读作(function foo)。
可以修改阅读器表格以自定义语言。表中的条目称为读取器宏。这个名称往往会使人们感到困惑,因为它们与defmacro定义的宏完全不同。它们共同提供了被称为“成长”的能力。语言[4]。
[4] http://www.catonmat.net/blog/growing-a-language-by-guy-steele/
答案 2 :(得分:3)
使用#'foo和'foo作为函数指示符的另一个区别是#'foo计算为函数对象,但'foo计算符号。因此,使用'foo将查找函数对象的工作转移到以后的时间。如果你在循环的每个循环中执行它而不是只执行一次,那么这可能是一个明显的性能损失。
CL-USER> (defun foo () 42)
FOO
CL-USER> (read-from-string "'foo")
=> (QUOTE FOO), 4
CL-USER> (eval *)
FOO
CL-USER> (read-from-string "#'foo")
=> (FUNCTION FOO), 5
CL-USER> (eval *)
=> #<FUNCTION FOO>
答案 3 :(得分:0)
经验告诉我,在一个由许多部分组成的大系统中,&#34;&#39;&#34;&#34;与&#34;#&#39;&#34;成语使修补更容易。 原因是每次遇到符号时都会查找与符号关联的函数对象,并在每个环境中查找。一旦您加载(当然是交互式)新定义(很可能是补丁),它就会在下次遇到时使用。性能成本非常小,但灵活性的优势是巨大的。在再次尝试应用程序时,想象一下客户的表情真是太好了。哇!哇!它现在有效!&#34; : - )