3

On Lisp(第 9 页)中,可以找到以下声明:

函数是 Lisp 程序的基石。它们也是 Lisp 的基石。在大多数语言中,+ 运算符与用户定义的函数完全不同。但是 Lisp 有一个单一的模型,函数应用程序,用来描述程序完成的所有计算。Lisp + 运算符是一个函数,就像您可以自己定义的那些一样。事实上,除了少数称为特殊形式的运算符外,Lisp 的核心是 Lisp 函数的集合。是什么阻止你添加到这个集合中?什么都没有:如果你想到了你希望 Lisp 能做的事情,你可以自己写,你的新函数将被视为内置函数。

我的问题是如何+使用以下特殊运算符来实现运算符之类的东西?还是实际上使用了更多运算符,而格雷厄姆只是不够精确和戏剧化?

block      let*                  return-from      
catch      load-time-value       setq             
eval-when  locally               symbol-macrolet  
flet       macrolet              tagbody          
function   multiple-value-call   the              
go         multiple-value-prog1  throw            
if         progn                 unwind-protect   
labels     progv                                  
let        quote    

有没有办法查看这些函数的源代码?

4

5 回答 5

6

他并不是说每个功能都是根据这些特殊形式实现的。

他说+(像其他所有功能一样)是一个功能

  • 可以使用正常语法调用它(函数(+ x y z)在哪里, , ,参数在哪里)。+xyz
  • 参数将首先被评估:(f (g) (h))将在调用之前gh之前调用f,即使f碰巧是+.
  • 您可以像任何其他函数一样传递它并通过funcall:apply(let ((x #'+)) (funcall x 1 2 3))6 来调用它。

关键是这些属性不一定适用于特殊形式。if不首先评估其所有论点;您不能引用let并间接调用它;等等

当然,这仍然为“编译器魔法”敞开了大门。在某些时候+必须执行低级操作,这取决于例如如何实现数字。根据您的 Lisp 编译器,细节看起来会有所不同。

于 2018-05-01T17:44:50.917 回答
4

cl:+只是表现得像一个函数,并被视为一个函数。

您不必自己定义它,也不必仅使用其他已定义的 CL 标准运算符来定义它。很可能定义cl:+使用实现特定的内部功能。

Common Lisp 有三种类型的操作符:函数特殊操作符

该标准没有太多说明它们是如何实施的。有理由相信 Common Lisp 实现将有额外的内部操作符用于实现标准操作符。

  • 有固定数量的特殊运算符- 尽管 CL 实现可能提供额外的特殊运算符。特殊运算符不是函数,用户无法定义特殊运算符。

  • CL 定义了一堆标准的宏以及用户定义新宏的方法。未指定如何定义标准宏。

  • CL 定义了一堆标准函数以及用户定义新函数的方法。未指定如何定义标准功能。

于 2018-05-01T17:58:19.217 回答
3

这是 src/code/numbers.lisp 中 sbcl 的源代码

(macrolet ((define-arith (op init doc)
             `(defun ,op (&rest numbers)
                (declare (explicit-check))
                ,doc
                (if numbers
                    (let ((result (the number (fast-&rest-nth 0 numbers))))
                      (do-rest-arg ((n) numbers 1 result)
                        (setq result (,op result n))))
                    ,init))))
  (define-arith + 0
    "Return the sum of its arguments. With no args, returns 0.")
  (define-arith * 1
    "Return the product of its arguments. With no args, returns 1."))
于 2018-05-01T17:44:01.297 回答
3

在实践层面上,您正确地观察到+仅使用核心特殊形式来实现并不是特别有效,而没有任何算术感知的低级实用程序(因为 CPU 往往非常擅长算术)。

我相信格雷厄姆的观点更多的是+,Common Lisp 中的函数没有任何固有的东西会阻止你自己定义它。在大多数语言中(C++ 是另一个例外),+它的实现方式与用户定义的函数完全不同。两个常见的原因是因为+通常使用中缀表示法,而函数调用使用func(arg1, arg2)表示法;并且通常不可能使用像加号这样的随机符号来定义新的用户创建的函数。

另一方面,Common Lisp 没有这个限制。+使用与任何其他函数相同的句法结构,因此该原因不适用;同样,Common Lisp 允许您在函数名称中使用许多不同的字符。

例如,这在 GNU 中可以正常工作(带有警告)clisp,而在 JavaScript 中则不可能做同样的事情:

(defun + (a b) (- a (- 0 b))) ; using '- to avoid having to look up other forms
于 2018-05-01T17:47:39.180 回答
2

在其他语言中,Algol 是最常见的,而 APL 可能是最明显的,使用户定义的抽象与该语言附带的功能和形式无法区分是非常特殊的。

因此,我认为您误读了文本。想象一下,您想要创建一个数字库,该库可以用间隔来扩展数字的语言概念。您创建一个库并定义一种创建间隔的方法,并使您的库在数字和间隔上工作。如果你有一个做数学的库,你可以在库中使用你的类,如果你已经定义了你需要的所有函数,它就会以间隔开箱即用。现在在大多数 Algol 语言中+, /, ... 不是函数而是运算符,它们有特殊的规则,所以即使 C++ 已经使运算符重载,也没有多少其他语言支持创建一个+适用于您的新间隔类型。解决方案将是显然看起来不像语言自己的原语的函数,因为它们是运算符。在 APL 中,所有原语都有特殊符号,而所有用户定义的都没有,所以所有用户定义的抽象都很容易看到,因为它们不是花哨的符号。

今天,我们经常发布 Java、JavaScript、C++ 等的新版本。这是必要的,因为这些语言没有办法抽象出语言中自己的语法。今天的 JavaScript 有 babel,它基本上为您提供了一种将一种语法转换为另一种语法的方法,但它还没有转换一种可以让您从语言中做到这一点的功能。这些是 Lisp 几十年来一直拥有的东西,现代语言正在获得 lisp 功能,但有些仍然很少。

我制作了一种 lisp 语言,它与第一个 lisp 一样缺少数字,因为我对它不感兴趣。我还必须用那种语言制作 99 瓶啤酒。我使用列表和符号并制作和定义+, -, zerop. 当您查看实际程序时,您看不到它缺少数字。

我喜欢 Guy L. Steele 从 98 发表的关于扩展编程语言的演讲。他是 Scheme 的原作者,曾参与过多种语言,包括 Java 和 Common Lisp。

于 2018-05-02T23:29:52.300 回答