lisp 如何决定是评估列表还是将其用作数据?

How does lisp decide whether to evaluate a list or use it as data?

如果你写类似 (+ 2 3) 的东西,+ 将被视为一个函数,如果 + 没有任何与之关联的函数定义,则会发生错误。

如果您写类似 (let (a b c)) 的内容,(a b c) 将被读取为数据。

lisp 是如何决定的?

这里有两个基本的 lisp 概念需要理解:

  1. 阅读与评价的区别
  2. 特殊形式
  3. 符号

阅读诉评估

阅读是将文本转换为lisp对象的行为。数字和列表等 Lisp 对象具有所谓的 阅读形式 ,这是一种可以读入对象的文本表示。它通常与打印表格相同。如果您来自 Java 等语言,toString() 给出对象的打印形式。阅读没有等价的概念,因为Lisp有有趣和强大的属性,Lisp源代码是一个lisp对象

需要注意的是,阅读不等于评价,虽然很多对象都是自我评价的。例如,一个数字是一个对象,在计算时,return 就是它本身。更常见的说法是许多 表格 自我评估,但您应该记住,上下文中的 "form" 通常与 "lisp object," 同义,暗示将评估对象。

考虑一下当我们在 REPL 中 运行 一个简单的程序时会发生什么可能会很有用。回想一下 "REPL" 代表 "Read, evaluate, print, loop",而 Lisp 是这些步骤之间的区别非常明显的语言。

考虑列表程序:

161

当你在 REPL 中 运行 时,结果如下:

161

这里发生的事情如下:

  1. : 读形式 "161" 转换为Lisp中的数字161的对象
  2. 评价:数字161为自评表。所以 161 的计算结果为 161.
  3. 打印:数字161的打印表示是“161”。所以这个数字被打印出来了。
  4. 循环: 再次提示输入。

符号:自评表

符号的阅读形式将是文本,如 abc。符号计算它们持有的变量。让我们 运行 REPL 中的程序 abc:

abc

这个程序实际上产生了错误:

eval-last-sexp-1: Symbol's value as variable is void: abc

这里发生的是:

  1. 读取了 s 表达式 "abc"
  2. 它是符号 abc 的读取形式,因此创建了该对象。
  3. 符号 abc 对其变量求值,但是,它当前没有变量(使用 "void"),因此它出错

特殊形式setq会将符号设置为指向一个变量。更多内容见下文。

特殊形式

从这里开始,Lisp 与大多数语言一样工作。调用 (+ 1 (+ 2 3)) 将执行与 plus(1, plus(2,3)) 相同的操作,在具有加号功能的命令式语言中。整个表达式将是红色的,以构建列表对象 (+ 1 (+ 2 3 )),这是一个包含三个元素的列表 +1 和列表 (+2 3)。然后,作为函数调用的外部 + 将导致解释器评估对象 1,它是自评估的,而 (+ 2 3) 最终将评估为 5。然后+ 将获取两个评估对象,15,并将它们相加,并且 return 6.

但是,Lisp 有所谓的特殊形式。特殊形式 可以跳过计算步骤 并只使用原始对象。例如,setq 是一种计算第二个参数而不是第一个参数的特殊形式。

(setq a (+ 2 3))

计算符号"a",但会将符号"a"赋值给(+ 2 3)计算的对象,即,数字 5。然后,调用

a

在 REPL 中将打印 5.

所以要回答最初的问题..."let" 是一种特殊形式,因此不会立即评估其参数:)

宏也不评估它们的论点,但对于原始问题可能没有必要进行全面讨论。

If you write something like (let (a b c)), (a b c) is read as data.

不是真的。一切都是一样的。首先阅读 Lisp 形式。完全。评估稍后进行。评估有所作为。

一个LET表达式是一种特殊的形式,该形式根据特殊的规则求值。

参见 Emacs Lisp 手册:Parts of let Expression

Lisp 有

  • 函数调用(* a b)
  • 特殊形式,如 (let ((a 1)) a)。这些表单通常是内置的,不能由用户扩展。
  • 宏形式喜欢(defun foo () nil)
  • 加上 Lisp 方言提供的任何其他内容...

标识符,即列表中的第一个元素,告诉我们如何评估表单。