如何使用特殊运算符/表单在Common Lisp中实现“+”

时间:2018-05-01 17:30:16

标签: function common-lisp special-form

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    

有没有办法查看这些功能的源代码?

5 个答案:

答案 0 :(得分:6)

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

他说{​​{1}}(和其他所有功能一样)是一个功能

  • 可以使用普通语法+调用它(其中(+ x y z)是函数,+xy参数。
  • 将首先评估参数:z会在调用(f (g) (h))之前同时调用gh,即使f恰好是{{1} }}。
  • 您可以像传输其他任何功能一样传递它,并通过f+调用它:funcall为6。

关键是这些属性不一定适用于特殊形式。 apply不首先评估所有论点;你不能参考(let ((x #'+)) (funcall x 1 2 3))并间接称呼它;等

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

答案 1 :(得分:4)

cl:+就像一个函数一样,被视为一个函数。

您无需自己定义或仅使用其他已定义的CL标准运算符定义它。 cl:+的定义很可能使用特定于实现的内部功能。

Common Lisp有三种类型的运算符:函数特殊运算符

标准没有详细说明它们的实施方式。有理由相信Common Lisp实现将有额外的内部运算符用于实现标准的运算符。

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

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

  • CL定义了一系列标准函数和用户定义新函数的方法。未指定如何定义标准函数。

答案 2 :(得分: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."))

答案 3 :(得分: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

答案 4 :(得分:2)

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

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

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

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

我喜欢Guy L. Steele在98年关于extending programming languages的演讲。他是Scheme的原作者,并参与过多种语言,包括Java和Common Lisp。