【问题标题】:Lisp: list vs S-expressionLisp:列表与 S 表达式
【发布时间】:2012-05-27 01:54:06
【问题描述】:

我是 Lisp 新手。我遇到了 2 个术语“列表”和“S 表达式”。我只是无法区分它们。它们只是 Lisp 中的同义词吗?

【问题讨论】:

标签: list lisp s-expression


【解决方案1】:

首先,不是所有的 S 表达式都代表列表; foobar 等表示裸原子的表达式也被视为 S 表达式。正如“cons cell”语法(car . cons),当“cons”部分本身不是另一个列表(或nil)时使用。更熟悉的列表表达式,例如 (a b c d),只是嵌套 cons 单元链的语法糖;该示例扩展为(a . (b . (c . (d . nil))))

其次,术语“S 表达式”指的是语法 - (items like this (possibly nested))。这样的 S 表达式是列表的 Lisp 源代码中的表示,但从技术上讲,它本身并不是列表。这种区别与十进制数字序列与其数值之间的区别,或者引号内的字符序列与结果字符串之间的区别相同。

这可能是一个过于技术性的区别;程序员经常引用值的文字表示,就好像它们就是值本身一样。但是对于 Lisp 和列表,事情会变得有点棘手,因为 Lisp 程序中的一切在技术上都是一个列表。

例如,考虑这个表达式:

(+ 1 2)

上面是一个简单的 S 表达式,它表示一个平面列表,由原子 +12 组成。

但是,在 Lisp 程序中,这样的列表将被解释为以 1 和 2 作为参数调用 + 函数。 (请注意,这样解释的是 list,而不是 S 表达式;评估者收到的列表是由读者预先解析的,而不是源代码文本。)

因此,虽然上面的 S 表达式表示一个列表,但在 Lisp 程序的上下文中,它很少被称为为“列表”。除非讨论宏,或者读者的内部工作,或者因为其他代码生成或解析上下文而进行元句法讨论,否则典型的 Lisp 程序员会将上述内容视为数字表达式。

另一方面,以下任何 S 表达式都可能 被称为“列表”,因为将它们评估为 Lisp 代码会生成由上述文字 S 表达式表示的列表作为运行时值:

'(+ 1 2)
(quote (+ 1 2))
(list '+ 1 2)

当然,代码和数据的等价性是 Lisp 的一大优点,所以区别是流动的。但我的观点是,虽然以上所有内容都是 S 表达式和列表,但在随意的 Lisp 语言中,只有一些会被称为“列表”。

【讨论】:

  • list 是一个带有 NIL sentinel 的 cons 细胞链。就是这样。 reader 将文本转换为 S 表达式——LISP 中的一切都是 S 表达式——它是原子或 cons 单元(向量是原子等)。读者makes no attempt to validate the argument for eval.
  • 阅读器不输出 S 表达式。 “S 表达式”指的是带有括号等的文本表示。当读者读完它时,它不再是一个 S 表达式,而已经是内存中的一个实际列表——你提到的“带有 nil sentinel 的 cons cell chain”。
【解决方案2】:

S 表达式是数据的表示法

历史上一个s-expressionsymbolic expression的缩写)被描述为:

  • 符号,例如 FOOBAR
  • cons 单元格 以 s 表达式作为其第一个和第二个元素:( expression-1 . expression-2 )
  • 列表终止符号NIL
  • 和一个写列表的约定:(A.(B.NIL))写成更简单的列表@9876

另请注意,历史上程序文本的编写方式不同。函数ASSOC 的示例。

assoc[x;y] =
   eq[caar[y];x] -> cadar[y];
   T -> assoc[x;cdr[y]]

历史上还存在从这些 m 表达式(元表达式的缩写)到 s 表达式的映射。如今,大多数 Lisp 程序代码都是使用 s 表达式编写的。

这里有描述:McCarthy, Recursive Functions of Symbolic Expressions

在如今的像 Common Lisp 这样的 Lisp 编程语言中,s-expressions 有更多的语法并且可以编码更多的数据类型:

  • 符号:symbol123|This is a symbol with spaces|
  • 号码:1231.01/3、...
  • 字符串:"This is a string"
  • 字符:#\a#\space
  • 矢量:#(a b c)
  • Conses 和列表:( a . b ), (a b c)
  • 评论:; this is a comment#| this is a comment |#

还有更多。

列表

列表是一个数据结构。它由 cons 单元格和列表结束标记组成。在 Lisp 中,列表具有 notation 作为 s 表达式中的列表。您可以对列表使用其他一些表示法,但在 Lisp 中,人们已经决定使用 s-expression 语法来编写它们。

旁注:程序和表格

在像 Common Lisp 这样的编程语言中,编程语言的表达式不是文本,而是数据!这与许多其他编程语言不同。编程语言 Common Lisp 中的表达式称为Lisp forms

例如,函数调用是 Lisp 数据,其中调用是一个列表,其中函数符号作为它的第一个元素,接下来的元素是它的参数。

我们可以写成(sin 3.0)。但它确实是数据。我们也可以构建数据。

评估 Lisp 表单的函数称为 EVAL,它采用 Lisp 数据,而不是程序文本或程序文本字符串。因此,您可以使用返回 Lisp 数据的 Lisp 函数构建程序:(EVAL (LIST 'SIN 3.0)) 的计算结果为 0.14112

由于 Lisp 表单具有数据表示,它们通常使用 Lisp 的外部数据表示来编写 - 这是什么? - s 表达式!

它是 s 表达式。 Lisp 表单作为 Lisp 数据被外部写入为 s 表达式。

【讨论】:

  • S 表达式排在第一位。它们从未打算成为程序员所看到的,而是一种中间表示。然而,想出顶级表示,M-expressions 花了一段时间,并没有给团队留下深刻印象。到那时,每个人都已经决定他们喜欢 S 表达式。因此,即使从历史上看,它也一直是 S 表达式。 M 表达式充其量只是一个脚注。
  • @Mark Reed:McCarthy 1960 年的论文描述了符号表达式和元表达式。
  • 确实如此,@rainerjoswig。我想我记错了。谢谢指正。
  • 你说的是“cons cells with expressions...”,不应该是“cons cells with s-expressions...”吗?
  • 当您写“表达式-1 . 表达式-2”时,您的意思是不是真的是 .. s-表达式-1 。 s-expression-2 ?
【解决方案3】:

您应该首先了解 Lisp 的主要功能 - 程序可以作为数据进行操作。与其他语言(如 C 或 Java)不同,您使用特殊语法({}classdefine 等)编写程序,在 Lisp 中,您将代码编写为(嵌套)列表(顺便说一句,这允许直接表达abstract syntactic trees)。再说一遍:你编写的程序看起来就像语言的数据结构。

当你把它当作数据来谈时,你称它为“列表”,但当你谈程序代码时,你应该更好使用术语“s-expression”。因此,从技术上讲,它们是相似的,但在不同的上下文中使用。混合这些术语的唯一真正地方是元编程(通常使用宏)。

还要注意,s 表达式也可能由唯一的 原子 组成(如数字、字符串等)。

【讨论】:

  • s-expression 在历史上不是程序代码,而是一种数据符号。列表是一种数据结构。请参阅 McCarthy 关于 Lisp 的原始论文。
  • @RainerJoswig:举个例子,看看 CLHS 的 mapcar 定义 - 它说“mapcar 对 列表 的连续元素进行操作”。列表,而不是 s 表达式。另一方面,您很少会看到短语“interpreter evaluates this list...”,在大多数情况下您会看到“interpreter evaluates s-expressions...”。这就是它们现在流行文学中的使用方式。尽管(我强调了这一点)这些术语非常相似,因此可能会在 Lisp 漫长的历史中产生干扰,尤其是在科学论文中。
  • @ffriend:当然,mapcar 在列表上工作。不,eval 不适用于 s 表达式。它适用于表示为数据的 Lisp 表单。
  • @RainerJoswig:这仅适用于 Common Lisp,但如果您愿意,可以在我的 cmets 中将“源代码”的每个条目更改为“源文件”,您应该会明白的。
  • 这里的要点,术语挑剔,是 Lisp eval 函数不对字符串进行操作;在这一点上,它与大多数现代动态语言中的同名函数不同。相反,它希望它的参数已经是代码的内存表示,即(分层嵌套的树)列表。 reader 将 S 表达式转换为要评估的列表。所以,S-表达式:列表的文本表示。序列化,实际上,与 JSON 或 XML 表示对象树的方式非常相似。
【解决方案4】:

S 表达式的简单定义是

(define S-expression?
    (λ (object)
        (or (atom? object) (list? object))))

;; Where atom? is:

(define atom?
  (λ (object) 
    (and (not (pair? object)) (not (null? object)))))

;; And list? is:

(define list? (λ (object)
  (let loop ((l1 object) (l2 object))
    (if (pair? l1)
        (let ((l1 (cdr l1)))
          (cond ((eq? l1 l2) #f)
                ((pair? l1) (loop (cdr l1) (cdr l2)))
                (else (null? l1))))
        (null? l1)))))

【讨论】:

    【解决方案5】:

    两者的写法相似:(blah blah blah),可能是嵌套的。有一个区别 - 列表以撇号为前缀。

    关于评估:

    • S 表达式返回一些结果(可能是原子或列表或 nil 或其他)
    • 列表返回列表

    如果需要,我们可以将列表转换为 s-exp,反之亦然。

    • (eval '(blah blah blah)) => list 被视为 s-exp 并返回结果。
    • (quote (blah blah blah)) => 将 sexp 转换为列表并返回列表而不进行评估

    国际会计准则:

    • 如果将 List 视为数据,则称为 List,如果将其视为代码,则称为 s-exp。

    【讨论】:

    • "如果将 List 视为数据,则称为 List,如果将其视为代码,则称为 s-exp。" IME,如果一个列表被视为数据,它被称为一个列表,否则它被称为不管它是什么——通常是一个函数调用。术语s-expression 保留用于以元文本方式专门讨论语法。
    • "IME" = 根据我的经验
    猜你喜欢
    • 2012-11-15
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 2018-03-02
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2011-12-24
    相关资源
    最近更新 更多