Lisp'的实例;什么是灵活性?

Lisp'的实例;什么是灵活性?,lisp,homoiconicity,Lisp,Homoiconicity,有人试图向我推销Lisp,它是一种超级强大的语言,可以做任何事情,甚至可以做一些事情 是否有一个实用的Lisp强大的代码示例?(最好与用常规语言编写的等效逻辑一起使用)。您是否已经了解了宏强大且灵活的原因?抱歉,没有其他语言的例子,但它可能会在宏上销售给您。我能想到的最好的例子是Paul Graham的书。完整的PDF可以从我刚才提供的链接下载。您也可以尝试(也可以在web上完全获得) 我有很多不切实际的例子。我曾经用大约40行lisp编写了一个程序,它可以解析自身,将其源代码视为lisp列表,

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


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

您是否已经了解了宏强大且灵活的原因?抱歉,没有其他语言的例子,但它可能会在宏上销售给您。

我能想到的最好的例子是Paul Graham的书。完整的PDF可以从我刚才提供的链接下载。您也可以尝试(也可以在web上完全获得)


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

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

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

如果[一种语言]能提高人们的生产力,那么他们就会使用 信息技术当其他语言出现时,这会更好(或者如果是的话) (这里已经有了),然后人们会转而使用这种语言。这是 法律,这是好的。法律告诉我这个计划(或任何其他口齿不清的语言) 方言)可能不是“正确”的语言:太多的人有 在过去的30年里,他们用脚投票。


您可能会发现这篇文章很有帮助:

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

@Mark

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

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

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


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

你可能会发现Eric Normand的文章很有帮助。他描述了随着代码库的增长,Lisp如何帮助您为应用程序构建语言。虽然这在早期通常需要额外的努力,但在以后会给您带来很大的优势。

实际上,一个很好的实用示例是Lisp循环宏

循环宏就是一个Lisp宏。但它基本上定义了一个小型循环DSL(领域特定语言)

浏览这个小教程时,您会发现(即使是新手)很难知道代码的哪一部分是循环宏的一部分,哪一部分是“普通”Lisp

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

比如说,在Java中,与您自己的代码,甚至是第三方代码库相比,您可能无法(一目了然)知道程序的哪一部分来自标准Java库,但您确实知道代码的哪一部分是Java语言,而不仅仅是类的方法调用。诚然,这都是“Java语言”,但作为程序员,您仅限于将应用程序表示为类和方法的组合(现在是注释)。而在Lisp中,所有的东西都可以抓取

考虑使用公共SQL接口将公共Lisp连接到SQL。这里,它们展示了如何扩展CL循环宏,使SQL绑定成为“第一类公民”

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

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

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

Common Lisp是为数不多的几个环境之一,在这些环境中,我们不仅可以自上而下地构建应用程序,而且可以将语言和环境提升到一半,我们可以在两端编写代码

注意,尽管这很优雅,但它不是平底锅
(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))
(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))
(defun ldap-attr (entity attr)
  `(,attr (car (ldap:attr-value ,entity ',attr))))
(defmacro with-ldap-attrs (attrs ent &rest body)
  `(let ,(loop for attr in attrs
         collecting `,(ldap-attr ent attr))
     ,@body)) 
(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))
(defmacro my-when (test &rest body)
  `(if ,test 
     (progn ,@body)))
;; one form
(if (numberp 1)
  (print "yay, a number"))

;; two forms
(if (numberp 1)
  (progn
    (assert-world-is-sane t)
    (print "phew!"))))
(my-when (numberp 1)
  (assert-world-is-sane t)
  (print "phew!"))
Lisp> (macroexpand-1 '(my-when (numberp 1)
                        (print "yay!")))

(if (numberp 1)
  (progn (print "yay!")))
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)
(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)))
(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>
(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>"
(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>"
(defmacro while (condition &body body)
  `(if ,condition
       (progn
         ,@body
         (do nil ((not ,condition))
           ,@body))))
(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