Lisp灵活性的实际例子?[关闭]


70

有人试图将Lisp卖给我,这是一种超级强大的语言,可以完成所有工作,然后再做一些。

是否有一个实际的Lisp的电源的代码示例?
(最好与以常规语言编码的等效逻辑一起使用。)


到目前为止,我只想对所有回答过的人表示感谢-我还没有机会正确地看过这个问题,也许有一段时间了,但是我很感激您的回答,一旦我明白了,我就会选择答案。有时间阅读和理解它们。:)
彼得·布顿

“最好与以常规语言编码的等效逻辑一起使用”,我希望有人这样做。我希望看到这样的比较表:coffeescript.org(左侧为CoffeeScript,右侧为已编译的JavaScript输出)
马修·洛克

Answers:


78

我喜欢宏。

这是用于从LDAP填充人员属性的代码。我只是碰巧发现那个代码四处乱窜而变了,对其他人有用。

有些人对宏的假定运行时惩罚感到困惑,因此我在末尾进行了一些尝试以澄清问题。

一开始就有重复

(defun ldap-users ()
  (let ((people (make-hash-table :test 'equal)))
    (ldap:dosearch (ent (ldap:search *ldap* "(&(telephonenumber=*) (cn=*))"))
                   (let ((mail  (car (ldap:attr-value ent 'mail)))
                         (uid   (car (ldap:attr-value ent 'uid)))
                         (name  (car (ldap:attr-value ent 'cn)))
                         (phonenumber (car (ldap:attr-value ent 'telephonenumber))))
                      (setf (gethash uid people)
                            (list mail name phonenumber))))
    people))

您可以将“ let绑定”视为一个局部变量,该变量在LET形式之外消失。注意绑定的形式-它们非常相似,只是LDAP实体的属性和绑定值的名称(“局部变量”)不同。有用,但有点冗长,并且包含重复项。

追求美丽

现在,如果我们不必进行所有重复操作,那岂不是很好吗?一个常见的习惯用法是WITH -...宏,它基于可以从中获取值的表达式来绑定值。让我们介绍一下自己的宏,WITH-LDAP-ATTRS,然后将其替换为原始代码。

(defun ldap-users ()
  (let ((people (make-hash-table :test 'equal))) ; equal so strings compare equal!
    (ldap:dosearch (ent (ldap:search *ldap* "(&(telephonenumber=*) (cn=*))"))
                   (with-ldap-attrs (mail uid name phonenumber) ent
                       (setf (gethash uid people)
                             (list mail name phonenumber))))
    people))

您是否看到一堆线突然消失,并被一条线取代了?这该怎么做?当然,使用宏-编写代码的代码!Lisp中的宏与使用预处理器在C / C ++中发现的宏完全不同:在这里,您可以运行生成Lisp代码的实际Lisp代码(而不是#definecpp中的绒毛),而在另一个之前代码被编译。宏可以使用任何真实的Lisp代码,即普通函数。基本上没有限制。

摆脱丑陋

因此,让我们看看这是如何完成的。为了替换一个属性,我们定义了一个函数。

(defun ldap-attr (entity attr)
  `(,attr (car (ldap:attr-value ,entity ',attr))))

反引号语法看上去有些毛茸茸,但是它很容易。当你调用LDAP-ATTRS,它会吐出包含列表attr(这是逗号),其次是car(“列表中的第一个元素”(利弊对,实际上),并且在事实上被调用函数first您也可以使用),该方法接收所返回的列表中的第一个值ldap:attr-value。因为这不是我们在编译代码时要运行的代码(获取属性值是我们在运行程序时要执行的操作),所以在调用之前不必添加逗号。

无论如何。继续进行到宏的其余部分。

(defmacro with-ldap-attrs (attrs ent &rest body)
  `(let ,(loop for attr in attrs
         collecting `,(ldap-attr ent attr))
     ,@body)) 

,@-syntax是把一个清单某处的内容,而不是实际的名单。

结果

您可以轻松地验证这将为您提供正确的东西。宏通常以这种方式编写:从要简化的代码(输出)开始,到要编写的代码(输入)开始,然后开始模制宏,直到输入提供正确的输出为止。该函数macroexpand-1将告诉您宏是否正确:

(macroexpand-1 '(with-ldap-attrs (mail phonenumber) ent
                  (format t "~a with ~a" mail phonenumber)))

评估为

(let ((mail (car (trivial-ldap:attr-value ent 'mail)))
      (phonenumber (car (trivial-ldap:attr-value ent 'phonenumber))))
  (format t "~a with ~a" mail phonenumber))

如果将扩展宏的LET绑定与开头的代码进行比较,您会发现它的格式相同!

编译时与运行时:宏与函数

宏是在编译时运行的代码,其附加的缺点是它们可以调用任何普通代码。功能函数或宏!它只是一个花哨的过滤器,它带有一些参数,应用了一些转换,然后向编译器提供了生成的s-exp。

基本上,它使您可以使用可以在问题域中找到的动词来编写代码,而不是使用该语言中的低级原语!作为一个愚蠢的例子,请考虑以下内容(如果when还不是内置的):

(defmacro my-when (test &rest body)
  `(if ,test 
     (progn ,@body)))

if是一个内置的原语,将只允许您在分支中执行一种表单,如果要拥有多个表单,那么,您需要使用progn::

;; one form
(if (numberp 1)
  (print "yay, a number"))

;; two forms
(if (numberp 1)
  (progn
    (assert-world-is-sane t)
    (print "phew!"))))

与我们的新朋友,my-when我们都可以a)如果我们没有错误的分支,则使用更合适的动词,以及b)添加隐式排序运算符,即progn::

(my-when (numberp 1)
  (assert-world-is-sane t)
  (print "phew!"))

但是,已编译的代码将永远不会包含my-when,因为在第一遍中,所有宏都会展开,因此不会涉及运行时的损失

Lisp> (macroexpand-1 '(my-when (numberp 1)
                        (print "yay!")))

(if (numberp 1)
  (progn (print "yay!")))

请注意,macroexpand-1只有一个扩展级别;(很可能,实际上是!)扩展可能会进一步下降。但是,最终您会遇到特定于编译器的实现细节,这些细节通常不是很有趣。但是继续扩展结果最终将为您提供更多详细信息,或者只是您的输入s-exp返回。

希望能澄清一些事情。宏是一个功能强大的工具,也是我喜欢的Lisp功能之一。


2
不错,尽管我会用&body代替&rest来表示函数的主体。它的评价基本相同,但意图更明确。
亚历克

18

我能想到的最好的例子是Paul Graham撰写的《论Lisp》一书。完整的PDF可以从我刚刚给出的链接下载。您也可以尝试实用的普通Lisp(也可以在网上完全获得)。

我有很多不切实际的例子。我曾经用大约40行lisp编写了一个程序,该程序可以对其进行解析,将其源视为lisp列表,对列表进行树遍历并构建一个表达式,如果该源中存在waldo标识符,则该表达式将评估为WALDO或评估为如果不存在瓦尔多,则为零。通过将对car / cdr的调用添加到已解析的原始源中来构造返回的表达式。我不知道如何用其他语言在40行代码中执行此操作。也许perl可以用更少的行来完成它。


14

您可能会发现这篇文章很有帮助:http : //www.defmacro.org/ramblings/lisp.html

就是说,很难,简短地给出Lisp功能的实际示例,因为Lisp仅在非平凡的代码中才能发挥作用。当您的项目增长到一定大小时,您将欣赏Lisp的抽象工具,并为使用它们感到高兴。另一方面,合理的短代码示例将永远不会给您令人满意的演示,说明Lisp的出色之处,因为其他语言的预定义缩写在小示例中看起来比Lisp在管理特定于领域的抽象上的灵活性更具吸引力。


13

实际上,一个很好的实际例子是Lisp LOOP宏。

http://www.ai.sri.com/pkarp/loop.html

LOOP宏就是Lisp宏。然而,它基本上定义了一种小型循环DSL(域特定语言)。

当您浏览该小教程时,您会发现(即使是新手)也很难知道代码的哪一部分是Loop宏的一部分,而哪一部分是“正常的” Lisp。

这是Lisps表达能力的关键组成部分之一,新的代码实际上无法与系统区分开。

例如,在使用Java时,您可能(乍看之下)无法知道程序的哪一部分来自标准Java库,而不是您自己的代码,甚至是第三方库,您也确实知道代码的哪一部分是Java语言,而不是简单地对类进行方法调用。当然,所有这些都是“ Java语言”,但是作为程序员,您只能将应用程序表示为类和方法(以及现在的注释)的组合。而在Lisp中,几乎所有内容都可以争夺。

考虑将Common Lisp连接到SQL的Common SQL接口。这里, http://clsql.b9.com/manual/loop-tuples.html,它们显示了如何扩展CL Loop宏以使SQL绑定成为“一流公民”。

您还可以观察到诸如“ [选择[名字] [姓氏]:从[雇员] ::按[姓氏]排列]]”的构造。这是CL-SQL程序包的一部分,并作为“阅读器宏”实现。

在Lisp中,您不仅可以制作宏来创建新的结构,例如数据结构,控制结构等。而且您甚至可以通过阅读器宏来更改语言的语法。在这里,他们使用阅读器宏(在这种情况下为'['符号)进入SQL模式,以使SQL像嵌入式SQL一样工作,而不是像许多其他语言一样仅用作原始字符串。

作为应用程序开发人员,我们的任务是将我们的流程和构造转换为处理器可以理解的形式。这意味着我们不可避免地不得不“谈论”计算机语言,因为它“无法理解”我们。

Common Lisp是为数不多的环境之一,在该环境中,我们不仅可以自上而下地构建应用程序,还可以提升语言和环境,以使我们半途而废。我们可以在两端进行编码。

介意,尽其优雅,这不是万能药。显然,还有其他因素会影响语言和环境选择。但这当然值得学习和使用。我认为学习Lisp是提高编程水平的好方法,即使使用其他语言也是如此。


有些人会争论LOOP作为“好”榜样的地位。也许DO或DOTIMES或RETURN-FROM是更好的示例。
亚伦2009年

1
但是,作为Lisp功能强大的实际例子,您几乎无法击败它。LOOP像反铲一样丑陋且有效。
JasonFruit

13

我喜欢通用Lisp对象系统(CLOS)和多方法。

大多数(如果不是全部)面向对象的编程语言都具有类和方法的基本概念。Python中的以下代码段定义了PeelingTool和Vegetable类(类似于Visitor模式):

class PeelingTool:
    """I'm used to peel things. Mostly fruit, but anything peelable goes."""
    def peel(self, veggie):
        veggie.get_peeled(self)

class Veggie:
    """I'm a defenseless Veggie. I obey the get_peeled protocol
    used by the PeelingTool"""
    def get_peeled(self, tool):
        pass

class FingerTool(PeelingTool):
  ...

class KnifeTool(PeelingTool):
  ...

class Banana(Veggie):
    def get_peeled(self, tool):
        if type(tool) == FingerTool:
            self.hold_and_peel(tool)
        elif type(tool) == KnifeTool:
            self.cut_in_half(tool)

你把 peel方法放在PeelingTool中,并让Banana接受它。但是,它必须属于PeelingTool类,因此仅当您具有PeelingTool类的实例时才可以使用它。

通用Lisp对象系统版本:

(defclass peeling-tool () ())
(defclass knife-tool (peeling-tool) ())
(defclass finger-tool (peeling-tool) ())

(defclass veggie () ())
(defclass banana (veggie) ())

(defgeneric peel (veggie tool)
  (:documentation "I peel veggies, or actually anything that wants to be peeled"))

;; It might be possible to peel any object using any tool,
;; but I have no idea how. Left as an exercise for the reader
(defmethod peel (veggie tool)
   ...)

;; Bananas are easy to peel with our fingers!
(defmethod peel ((veggie banana) (tool finger-tool))
  (with-hands (left-hand right-hand) *me*
    (hold-object left-hand banana)
    (peel-with-fingers right-hand tool banana)))

;; Slightly different using a knife
(defmethod peel ((veggie banana) (tool knife-tool))
  (with-hands (left-hand right-hand) *me*
    (hold-object left-hand banana)
    (cut-in-half tool banana)))

可以用图灵完整的任何语言编写任何内容;语言之间的差异是您必须跳过多少圈才能获得同等的结果。

诸如Common Lisp之类的功能强大的语言具有宏和CLOS之类的功能,可让您快速而轻松地获得结果,而不必经历很多麻烦而无法满足于劣等解决方案,或者发现自己成为袋鼠。


7
什么?您可以用图灵完备的语言写东西吗?不。您的意思可能是可以用图灵完整语言完成的任何事情,也可以用另一种图灵完整语言完成的事情。
egaga

12

Lisp中有很多杀手级功能,但是宏是我特别喜欢的宏,因为在语言定义和定义之间不再存在障碍。例如,Common Lisp没有while结构。我曾经在走路时脑海里实现了它。简单明了:

(defmacro while (condition &body body)
  `(if ,condition
       (progn
         ,@body
         (do nil ((not ,condition))
           ,@body))))

等等!您只是使用新的基本结构扩展了Common Lisp语言。您现在可以执行以下操作:

(let ((foo 5))
  (while (not (zerop (decf foo)))
    (format t "still not zero: ~a~%" foo)))

哪个会打印:

still not zero: 4
still not zero: 3
still not zero: 2
still not zero: 1

读者可以使用任何非Lisp语言来完成此操作...


1
我必须在上面的“(let ...”)块中添加两个尾随的))s,然后才能对其进行评估,但是示例可以正常工作。谢谢!
丹尼尔J.普里切特

我想说的是我在c中有while的实现,它有一定的局限性(您必须使用WHILE / ENDWHILE而不是while(){},在同一行上不允许WHILE()WHILE()。 。),但它基本上可以正常工作,它使用goto和ifstatement以及一个堆栈来实现它,我想将其粘贴到此处,但变得丑陋。pastebin.com/0D6BAX2w
10年

@flownt:这就是任何其他语言的要点,将其扩展很丑陋,根本不适合。在Lisp中,您无法将原始构造与用户提供的原始构造区分开。在许多Common Lisp实现中,面向对象实际上是由Lisp代码添加到核心实现中的!

3
为什么这么复杂?While可以用(defmacro while(condition&body body)`(do()((not,condition)),@ body))定义...
6502 2012年

9

我发现这篇文章很有趣:

编程语言比较:Lisp与C ++

本文的作者Brandon Corfman写了一篇研究,该研究将Java,C ++和Lisp中的解决方案与编程问题进行了比较,然后编写了自己的C ++解决方案。基准解决方案是Peter Norvig的45行Lisp(在2小时内编写)。

Corfman发现很难将其解决方案减少到少于142行C ++ / STL。他对原因的分析很有趣。



8

我最喜欢Lisp(和Smalltalk)系统的地方是,它们感觉很活跃。您可以在Lisp系统运行时轻松地对其进行探测和修改。

如果这听起来很神秘,请启动Emacs,然后输入一些Lisp代码。输入C-M-x并贴上!您只是从Emacs内部更改了Emacs。您可以继续运行,然后重新定义其所有Emacs功能。

另一件事是,代码=列表的等效性使代码和数据之间的边界非常稀疏。而且由于有了宏,扩展语言和制作快速DSL变得非常容易

例如,可以对基本的HTML构建器进行编码,该构建器的代码与生成的HTML输出非常接近:

(html
  (head
    (title "The Title"))
  (body
    (h1 "The Headline" :class "headline")
    (p "Some text here" :id "content")))

=>

<html>
  <head>
    <title>The title</title>
  </head>
  <body>
    <h1 class="headline">The Headline</h1>
    <p id="contents">Some text here</p>
  </body>
</html>

在Lisp代码中,自动缩进使代码看起来像输出,但没有任何结束标记。


4
我必须第二次处理这种模糊不清的“感觉还活着”的东西。在(通用)Lisp或Smalltalk中工作确实与在我发现很难表达的方式下与其他语言工作有所不同(并且要优越得多)。
Frank Shearar 2010年

7

我喜欢来自http://common-lisp.net/cgi-bin/viewcvs.cgi/cl-selenium/?root=cl-selenium 的宏示例。这是对Selenium(Web浏览器测试框架)的Common Lisp绑定,但是它没有映射每个方法,而是在编译时读取Selenium自己的API定义XML文档,并使用宏生成映射代码。您可以在此处查看生成的API:common-lisp.net/project/cl-selenium/api/selenium-package/index.html

这实际上是在驱动带有外部数据的宏,在这种情况下,外部数据恰好是XML文档,但是从数据库或网络中读取数据可能非常复杂。这是在编译时为您提供整个Lisp环境的力量。


7

了解如何使用XML模板扩展Common Lispcl-准报价XML示例项目页面

(babel:octets-to-string
 (with-output-to-sequence (*html-stream*)
   <div (constantAttribute 42
         someJavaScript `js-inline(print (+ 40 2))
         runtimeAttribute ,(concatenate 'string "&foo" "&bar"))
     <someRandomElement
       <someOther>>>))

 =>

 "<div constantAttribute=\"42\"
       someJavaScript=\"javascript: print((40 + 2))\"
       runtimeAttribute=\"&amp;foo&amp;bar\">
    <someRandomElement>
      <someOther/>
    </someRandomElement>
  </div>"

这基本上与Lisp的反引号阅读器(用于列表准引用)相同,但是它也适用于其他各种事物,例如XML(安装在特殊的<>语法上),JavaScript(安装在`js-inline上)等。

为了清楚起见,这是在用户库中实现的!它将静态XML,JavaScript等部分编译为UTF-8编码的立即字节数组,这些数组可随时写入网络流。用简单,(逗号),您可以回到lisp并将运行时生成的数据交织到文字字节数组中。

这不是出于胆小,而是库将以上内容编译为:

(progn
 (write-sequence
  #(60 100 105 118 32 99 111 110 115 116 97 110 116 65 116 116 114 105 98
    117 116 101 61 34 52 50 34 32 115 111 109 101 74 97 118 97 83 99 114
    105 112 116 61 34 106 97 118 97 115 99 114 105 112 116 58 32 112 114
    105 110 116 40 40 52 48 32 43 32 50 41 41 34 32 114 117 110 116 105
    109 101 65 116 116 114 105 98 117 116 101 61 34)
  *html-stream*)
 (write-quasi-quoted-binary
  (let ((*transformation*
          #<quasi-quoted-string-to-quasi-quoted-binary {1006321441}>))
    (transform-quasi-quoted-string-to-quasi-quoted-binary
      (let ((*transformation*
               #<quasi-quoted-xml-to-quasi-quoted-string {1006326E51}>))
        (locally
            (declare (sb-ext:muffle-conditions sb-ext:compiler-note))
         (let ((it (concatenate 'string "runtime calculated: " "&foo" "&bar")))
           (if it
               (transform-quasi-quoted-xml-to-quasi-quoted-string/attribute-value it)
               nil))))))
  *html-stream*)
 (write-sequence
  #(34 62 10 32 32 60 115 111 109 101 82 97 110 100 111 109 69 108 101 109
    101 110 116 62 10 32 32 32 32 60 115 111 109 101 79 116 104 101 114 47
    62 10 32 32 60 47 115 111 109 101 82 97 110 100 111 109 69 108 101 109
    101 110 116 62 10 60 47 100 105 118 62 10)
  *html-stream*)
 +void+)

作为参考,上面的两个大字节向量在转换为字符串时如下所示:

"<div constantAttribute=\"42\"
      someJavaScript=\"javascript: print((40 + 2))\"
      runtimeAttribute=\""

第二个:

"\">
 <someRandomElement>
    <someOther/>
  </someRandomElement>
</div>"

而且它与其他Lisp结构(如宏和函数)很好地结合在一起。现在,将此与JSP进行比较...


7

我在1970年代是麻省理工学院的AI学生。像其他所有学生一样,我认为语言至关重要。尽管如此,Lisp是主要语言。这些是我仍然认为非常适合的一些事项:

  • 符号数学。编写表达式的符号差异和代数简化是容易且有启发性的。即使我用C语言做它们,我仍然会做那些。

  • 定理证明。我不时地进行临时的AI狂欢,就像试图证明插入排序是正确的。为此,我需要进行符号操作,通常我会回到Lisp。

  • 特定领域的语言很少。我知道Lisp并不是实用,但是如果我想尝试一点DSL而不必全都解析等,那么Lisp宏就可以轻松实现。

  • 像minimax游戏树搜索之类的小游戏算法可以在三行中完成。

  • 想尝试lambda演算吗?在Lisp中很容易。

Lisp为我做的主要是心理锻炼。然后,我可以将其延续到更实用的语言中。

PS说到演算,什么也开始在20世纪70年代,在同一AI millieu的,是OO开始侵入每个人的大脑,并以某种方式,它有什么兴趣似乎它是什么已经挤占了很大的兴趣为好。即在机器学习,自然语言,视觉,解决问题等方面的工作都排到了房间的后面,而课堂,消息,类型,多态性等则排在了最前面。


3
您至少说两次Lisp不切实际。为什么不呢?Clojure不切实际吗?您的实用标准是什么?
托德·斯托特

@Todd:我的实用标准是您可以用它来构建产品,分发它,确信它不会在现场出现故障,具有足够的性能,能够处理大量数据,拥有一个不错的可靠UI。我爱Lisp。对于像AI这样的实验软件,它很棒。
Mike Dunlavey

5
ITA软件是Kayak,Orbitz,Bing travel,American Airlines等旅行网站背后的引擎,它使用Lisp进行搜索引擎,数据处理以及部分网页渲染。这当然可以视为“具有足够的性能”的“处理大量数据”。它因其“漂亮,可靠的UI”而与Java共享。我不否认您的观点,即Lisp对于实验软件也非常有用。参见:paulgraham.com/carl.html
SuperElectric 2010年

2
@SuperElectric:好吧,我印象深刻。感谢更新。
Mike Dunlavey 2010年

@SuperElectric People似乎只提出了“ ITA Software”的示例,这是Graham的话题。
Unknow0059

6

我喜欢的一件事是我可以在不丢失应用程序状态的情况下升级代码“运行时”。它仅在某些情况下有用,但是当它有用时,已经存在(或在开发过程中仅花费最小的成本)要比从头开始实现便宜得多。特别是因为这要付出“几乎没有”的代价。


2
这是开发过程中的杀手feature!
路易斯·奥利维拉

5

你已经采取了看看这个为什么宏是强大和灵活的解释吗?抱歉,没有其他语言的示例,但是可能会在宏上卖给您。


如果您引用了该网站而不是仅仅链接它,那就太好了。
Unknow0059

4

@标记,

虽然您说的话有些道理,但我相信这并不总是那么简单。

程序员和人们通常并不总是花时间评估所有可能性并决定切换语言。通常是由经理来决定,还是由学校来教授第一门语言……而程序员永远不需要花费足够的时间来达到特定水平,因为他们可以决定使用这种语言比使用这种语言可以为我节省更多的时间。

另外,您必须承认,与没有大型语言支持的语言(例如Microsoft或Sun)相比,在市场上将始终具有优势。

为了回答最初的问题,保罗·格雷厄姆(Paul Graham)尝试在此处举一个例子尽管我承认它不一定像我想要的那样实用:-)


4

让我印象深刻的一件事是,如果您碰巧不喜欢随附的CLOS,则可以编写自己的面向对象的程序扩展。

其中之一在石榴石中,另一在Paul Graham的《On Lisp》中

还有一个名为Screamer的软件包,该软件包允许进行不确定性编程(我尚未评估)。

任何允许您对其进行更改以支持不同编程范例的语言都必​​须具有灵活性。


2

您可能会发现Eric Normand的这篇文章很有帮助。他描述了随着代码库的增长,Lisp可以帮助您根据应用程序构建语言,从而为您提供帮助。尽管这通常在早期需要付出额外的努力,但稍后会为您带来很大的好处。


2

它是一种多范式语言这一简单事实使它非常灵活。


-1

John Ousterhout在1994年对Lisp提出了一个有趣的观察:

语言设计者喜欢争论为什么这种语言或某种语言 必须先验地更好或更糟,但是这些论点都没有太大关系。最终,当用户用脚投票时,所有语言问题都得到解决。

如果[一种语言]使人们更有生产力,那么他们将使用它。当出现其他更好的语言(或者如果已经出现)时,人们将切换到该语言。这是法律,这很好。法律对我说,Scheme(或任何其他Lisp方言)可能不是“正确”的语言:在过去30年中,有太多人用脚投票。

http://www.vanderburg.org/OldPages/Tcl/war/0009.html


3
废话。该论点假定多数人总是对的。很难有一个坚实的前提。
troelskn

3
此外,它假设所有工具都具有相同的功能,只是具有不同的学习阈值。没有什么比这更错了。
Mikael Jansson

1
是的,无数蝇类的情况...没错... :)我从汇编到使用所有主流语言的Lisp都走了很长一段路,而我最终获得Lisp正是为了提高我的生产力。 ..并且我们进行企业应用程序。是的,常见于口齿不清。
阿提拉·伦德瓦

4
Ousterhout的观察实际上很草率。对于当前的普通程序员来说,lisp并不是一种好的普通语言,这是一个很好的论点,它忽略了语言设计和当前普通程序员共同发展的事实(一方面是对可互换的程序员单元的压力,另一方面是对语言的压力)。功率),更重要的是错过了这些平均值通常不是很有趣的事实。有时候,“其他人都在做”是一个很好的主意,其他时候根本没有。
西蒙
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.