【问题标题】:Practical example of Lisp's flexibility? [closed]Lisp 灵活性的实际例子? [关闭]
【发布时间】:2010-09-11 11:41:44
【问题描述】:

有人试图向我推销 Lisp,它是一种超级强大的语言,它可以做任何事情,而且还可以做一些事情。

是否有一个 实用 Lisp 强大功能的代码示例?
(最好与用常规语言编码的等效逻辑一起使用。)

【问题讨论】:

  • 只是简短地感谢到目前为止所有回答的人 - 我还没有机会正确看待这个问题,并且可能有一段时间没有机会,但我非常感谢您的回复和一旦我有时间阅读并理解它们,就会选择一个答案。 :)
  • “最好与用常规语言编码的等效逻辑一起”我希望有人会这样做。我想看一个像这样的比较表:coffeescript.org(左边是CoffeeScript,右边是编译好的JavaScript输出)

标签: lisp homoiconicity


【解决方案1】:

你有没有看过this解释为什么宏是强大和灵活的?抱歉,没有其他语言的示例,但它可能会向您推销宏。

【讨论】:

  • 如果您引用该网站而不是仅仅链接它会很好。
【解决方案2】:

我能想到的最好的例子是 Paul Graham 的书,On Lisp。可以从我刚刚提供的链接下载完整的 PDF。你也可以试试Practical Common Lisp(也完全可以在网络上找到)。

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

【讨论】:

    【解决方案3】:

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

    语言设计师喜欢争论为什么这种语言或那种语言 必须先验地更好或更坏,但这些论点都不是真正的 很重要。最终,当用户投票时,所有语言问题都会得到解决 用他们的脚。

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

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

    【讨论】:

    • 废话。该论点假设大多数人总是正确的。几乎不是一个非常可靠的前提。
    • 此外,它假设所有工具都同样强大,只是具有不同的学习阈值。没有比这更糟糕的了。
    • 是的,有无数苍蝇的情况......这不会是错的...... :) 我通过所有主流语言从汇编到 lisp 走了很长一段路,我最终使用 lisp 正是为了提高我的工作效率……我们做企业应用程序。是的,在普通的 lisp 中。
    • Ousterhout 的观察实际上相当草率。对于今天的普通程序员来说,lisp 不是一种好的普通语言,这是一个很好的论据,它忽略了语言设计和当前普通程序员共同发展的事实(一方面对可互换的程序员单元的压力,另一方面对语言的压力)功率),更重要的是忽略了这些平均值通常不是很有趣的事实。有时“其他人都在做”是一个好主意,有时则根本不遵循。
    【解决方案4】:

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

    也就是说,要给出 Lisp 强大功能的简短实用示例非常非常困难,因为它真的只在不平凡的代码中才能发挥作用。当您的项目发展到一定规模时,您将欣赏 Lisp 的抽象工具并为您一直在使用它们而感到高兴。另一方面,相当短的代码示例永远不会让您满意地展示 Lisp 的出色之处,因为其他语言的预定义缩写在小示例中看起来比 Lisp 在管理特定领域抽象方面的灵活性更具吸引力。

    【讨论】:

      【解决方案5】:

      @马克,

      虽然你所说的有些道理,但我相信它并不总是那么直截了当。

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

      此外,您必须承认,与没有此类支持的语言相比,有微软或 Sun 等大型商业实体支持的语言在市场上始终具有优势。

      为了回答最初的问题,Paul Graham 尝试举一个例子 here 尽管我承认它不一定像我想要的那样实用 :-)

      【讨论】:

        【解决方案6】:

        您可能会发现 Eric Normand 的 this post 很有帮助。他描述了随着代码库的增长,Lisp 如何帮助您将语言构建到您的应用程序中。虽然这通常需要在早期付出额外的努力,但它会在以后为您提供很大的优势。

        【讨论】:

          【解决方案7】:

          实际上,一个很好的实际例子是 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 循环宏以使 SQL 绑定成为“一等公民”。

          您还可以观察诸如“[select [first-name] [last-name] :from [employee] :order-by [last-name]]”之类的结构。这是 CL-SQL 包的一部分,作为“阅读器宏”实现。

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

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

          Common Lisp 是为数不多的环境之一,我们不仅可以自上而下构建应用程序,而且可以提升语言和环境以适应我们的一半。我们可以在两端编码。

          请注意,尽管如此优雅,但它不是灵丹妙药。显然还有其他因素会影响语言和环境的选择。但这当然值得学习和玩耍。我认为学习 Lisp 是提升编程水平的好方法,即使使用其他语言也是如此。

          【讨论】:

          • 有些人会争论 LOOP 是否是一个“好”的例子。也许 DO、DOTIMES 或 RETURN-FROM 会是更好的例子。
          • 但作为 Lisp 强大功能的一个实际例子,你几乎无法击败它。 LOOP 丑陋而有效,就像反铲一样。
          【解决方案8】:

          我喜欢宏。

          这里是从 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++ 中通过使用预处理器找到的宏完全不同:在这里,您可以运行 real Lisp 代码(不是 #define fluff in cpp) 在编译其他代码之前生成 Lisp 代码。宏可以使用任何真正的 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-bindings 与开头的代码进行比较,你会发现它的形式是一样的!

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

          宏是在编译时运行的代码,具有额外的变化,他们可以随意调用任何普通函数或宏!它只不过是一个花哨的过滤器,接受一些参数,应用一些转换,然后将生成的 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 中的一个特性。

          【讨论】:

          • 不错,虽然我会使用 &body 而不是 &rest 作为函数的主体。它基本上评估相同,但使意图更清晰。
          【解决方案9】:

          我喜欢Common Lisp Object System (CLOS) 和多方法。

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

          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 类的实例时才能使用它。

          Common 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 等功能,可让您快速轻松地获得结果,而无需跳过太多的障碍,以至于您要么满足于低于标准的解决方案,要么发现自己变成了一只袋鼠。

          【讨论】:

          • 什么?你可以用图灵完备的语言写任何东西吗?不。您的意思可能是任何可以用图灵完备语言完成的事情,也可以用另一种图灵完备语言完成。
          【解决方案10】:

          我最喜欢 Lisp(和Smalltalk)系统的一点是,它们给人一种充满活力的感觉。您可以在 Lisp 系统运行时轻松探测和修改它们。

          如果这听起来很神秘,请启动 Emacs,然后输入一些 Lisp 代码。输入C-M-x,瞧!您刚刚从 Emacs 中更改了 Emacs。您可以在运行时继续并重新定义所有 Emacs 函数。

          另一件事是代码=列表等价性使代码和数据之间的边界非常细。并且多亏了宏,扩展语言并快速生成DSLs 非常容易。

          例如,可以编写一个基本的 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 代码中,自动缩进使代码看起来像输出,除了没有任何结束标记。

          【讨论】:

          • 我必须支持这种模糊的“感觉还活着”的东西。使用(通用)Lisp 或 Smalltalk 与使用其他语言以我难以表达的方式工作确实感觉不同(而且优越得多)。
          【解决方案11】:

          了解如何使用 XML 模板扩展 Common Lisp:cl-quasi-quote XML example、project page、

          (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 结构如宏和函数结合得很好。现在,将其与JSPs 进行比较...

          【讨论】:

            【解决方案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 语言中这样做都留给读者作为练习......

            【讨论】:

            • 我必须在上面的 "(let..." 块中添加两个尾随 )),然后它才能进行评估,但示例有效。谢谢!
            • 我想说我在这里有一个 while 在 c 中的实现,它有一些限制(你必须使用 WHILE / ENDWHILE 而不是 while(){},WHILE() WHILE() on不允许同一行...),但它基本上可以工作,它使用 goto 和带有堆栈的 if 语句来实现它,我想将它粘贴到这里,但结果变得丑陋。 pastebin.com/0D6BAX2w
            • @flynt:这就是重点,在任何其他语言中,扩展它都是丑陋的,根本不适合。在 Lisp 中,您无法区分原始构造和用户提供的构造。在许多 Common Lisp 实现中,面向对象实际上是通过 Lisp 代码添加到核心实现中的!
            • 为何如此复杂? While 可以定义为 (defmacro while (condition &body body) `(do ()((not ,condition)) ,@body)) ...
            【解决方案13】:

            我觉得这篇文章很有趣:

            Programming Language Comparison: Lisp vs C++

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

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

            【解决方案14】:

            我喜欢的一件事是我可以“运行时”升级代码而不会丢失应用程序状态。这只是在某些情况下有用的东西,但是当它有用时,已经存在它(或者,在开发过程中只需最低成本)比必须从头开始实现它便宜得多。尤其是因为这是“几乎没有”成本。

            【讨论】:

            • 这是开发过程中的杀手级功能!
            【解决方案15】:

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

            【讨论】:

              【解决方案16】:

              我是 1970 年代麻省理工学院的一名 AI 学生。和其他学生一样,我认为语言是最重要的。尽管如此,Lisp 是主要的语言。这些是我仍然认为它非常适合的一些事情:

              • 符号数学。编写表达式的符号微分和代数化简既简单又有益。我仍然做这些,即使我用 C 语言做这些。

              • 定理证明。我时不时地进行一次临时的 AI 狂欢,比如试图证明插入排序是正确的。为此,我需要进行符号操作,而且我通常会使用 Lisp。

              • 小领域特定语言。我知道 Lisp 真的 不实用,但如果我想尝试一下 DSL 而不必全神贯注于解析等,Lisp 宏会让它变得简单。

              • 像极小极大博弈树搜索这样的小游戏算法可以用三行代码完成。

              • 想试试lambda calculus?在 Lisp 中很容易。

              Lisp 为我做的主要是脑力锻炼。然后我可以把它带到更实用的语言中。

              附:说到 lambda 演算,同样始于 1970 年代,在同一个 AI 领域,OO 开始侵入每个人的大脑,不知何故,对它是什么的兴趣似乎挤出了对什么的兴趣。这对有好处。 IE。机器学习、自然语言、视觉、问题解决等方面的工作都放在了房间的后面,而类、消息、类型、多态性等都放在了前面。

              【讨论】:

              • 您至少两次声明 Lisp 不实用。为什么不是? Clojure 不实用吗?你的实用标准是什么?
              • @Todd:我的实用标准是你能用它构建一个产品,分发它,确信它不会在该领域失败,有足够的性能,能够处理山数据,有一个很好的可靠的用户界面。我喜欢 Lisp。对于像 AI 这样的实验性软件来说,这很棒。
              • ITA 软件是 Kayak、Orbitz、Bing travel、American Airlines 等旅游网站背后的引擎,它使用 Lisp 进行搜索引擎、数据处理和一些网页渲染。这当然有资格以“足够的性能”来“处理大量数据”。它与 Java 共享“漂亮、可靠的 UI”。我不否认你的说法,即 Lisp 也非常适合实验性软件。见:paulgraham.com/carl.html
              • @SuperElectric:嗯,我印象深刻。感谢您的更新。
              • @SuperElectric 人们似乎只提到过“ITA Software”的例子,这是格雷厄姆的话题。
              【解决方案17】:

              如果您碰巧不喜欢随附的 CLOS,那么让我印象深刻的一个具体事情是编写自己的面向对象编程扩展的能力。

              其中一个在Garnet,一个在 Paul Graham 的On Lisp。

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

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

              【讨论】:

                【解决方案18】:

                我喜欢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 环境的强大之处。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2017-12-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-04-13
                  • 1970-01-01
                  • 2010-11-23
                  • 1970-01-01
                  • 2010-10-06
                  相关资源
                  最近更新 更多