+、/ 和 * 的符号值

SYMBOL-VALUE of +,/ and *

考虑以下代码片段:

[1]> (symbol-value '+)
NIL
[2]> +
(SYMBOL-VALUE '+)
[3]> (symbol-value '/)
((SYMBOL-VALUE '+))
[4]> (symbol-value '+)
(SYMBOL-VALUE '/)
[5]> *
(SYMBOL-VALUE '/)

所以,根据我的观察,


这是什么原因?

似乎没有什么理由去检查你的最后输入、最后输出是什么,因为它已经在 REPL 上了。
那么,这些运营商的这种行为有什么具体原因吗? 这些运算符的symbol-value有什么历史意义吗?
任何帮助表示赞赏。

根据 HyperSpec:

The variables *, **, and *** are maintained by the Lisp read-eval-print loop to save the values of results that are printed each time through the loop.

The value of * is the most recent primary value that was printed, the value of ** is the previous value of *, and the value of *** is the previous value of **.

对于/ and friends

The variables /, //, and /// are maintained by the Lisp read-eval-print loop to save the values of results that were printed at the end of the loop.

The value of / is a list of the most recent values that were printed, the value of // is the previous value of /, and the value of /// is the previous value of //.

最后 + and friends:

The variables +, ++, and +++ are maintained by the Lisp read-eval-print loop to save forms that were recently evaluated.

The value of + is the last form that was evaluated, the value of ++ is the previous value of +, and the value of +++ is the previous value of ++.

你问的是:

What is the reason for this?

REPL 是一个交互式 textual 环境:在许多这样的系统中,比如 unix shell,总是有一些方便的方法来重复上一个命令,或者使用上一个值,无需明确复制这些值或重新输入它们(例如考虑旧的文本终端,其中没有 copy/paste 操作可用)。所以这些变量是那个时代的遗物(但是,尽管如此,对于像我这样的懒惰的人来说仍然有用,他们首先尝试获取某个值,然后,例如,用一个简单的方法将这样的值分配给一个全局变量击键 *,而不必复制和粘贴打印的值)。

符号值

(symbol-value '*some-special-var*)*some-special-var*没有区别。尽管它们是不同的表达式,但它们都计算相同的值。

CL-USER 47 > 1
1

CL-USER 48 > 2
2

CL-USER 49 > 3
3

CL-USER 50 > (list (list *   (symbol-value '*  ))
                   (list **  (symbol-value '** ))
                   (list *** (symbol-value '***)))
((3 3) (2 2) (1 1))

这些变量的一个原因:在 REPL 中重用不可读对象

在 Lisp 中,可以创建无法以可读方式打印的对象。 Lisp 不仅是一种文本语言,还是一种数据语言。为最后的结果、输入等设置变量使得重用这些对象稍微方便一些。一些 Lisp 侦听器具有更广泛的机制来引用对象或对象的一部分。 Common Lisp 标准化了一个简单的变体。

没有或不完整可读文本打印表示的对象的示例是 CLOS 对象、闭包和函数对象、流、哈希表、一些数组、外部数据……可以打印这些类型的对象,但输出reader 将无法使用它来取回原始对象或重建类似的对象。

通常可以回读文本表示,但不会创建相同的对象 - 有时这很有用。

CL-USER 41 > (lambda (a) (1+ a))
#<anonymous interpreted function 4060000984>

CL-USER 42 > 42
42

CL-USER 43 > (funcall ** *)
43

如您所见,在上面的示例中,如果只是文本,则无法粘贴和读回计算结果 41。

CL-USER 44 > #<anonymous interpreted function 4060000984>

Error: Subcharacter #\< not defined for dispatch char #\#.
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

必须有某种机制来记住它的值。将结果分配给标准变量是一种方法。另一种选择是复制子表单并重新计算对象:

CL-USER 45 > (funcall (lambda (a) (1+ a)) 42)
43

但这在某些情况下不起作用(结果可能无法重现)或可能需要大量计算时间(取决于执行的计算)。

注意:保留这些对对象的引用可防止对象在这些引用存在时被垃圾回收。