当前位置:  开发笔记 > 编程语言 > 正文

Lisp灵活性的实际例子?

如何解决《Lisp灵活性的实际例子?》经验,为你挑选了13个好方法。

有人试图向我出售Lisp,作为一种超级强大的语言,可以做任何事情,然后一些.

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



1> Mikael Janss..:

我喜欢宏.

这是从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 binding"视为局部变量,它会在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中的fluff)代码已编译.宏可以使用任何真正的Lisp代码,即普通函数.基本上没有限制.

摆脱丑陋

那么,让我们看看这是如何完成的.要替换一个属性,我们定义一个函数.

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

反引用语法看起来有点毛茸茸,但它的作用很简单.当你调用LDAP-ATTRS时,它会吐出一个包含(逗号)的列表attr,然后是car("列表中的第一个元素"(实际上是cons对),并且实际上有一个函数被调用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-exps.

基本上,它允许您在问题域中可以找到的动词中编写代码,而不是语言中的低级原语!作为一个愚蠢的例子,请考虑以下(如果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作为函数的主体.它基本上评估相同,但使意图更清晰.

2> Jason Dagit..:

我能想到的最好的例子是Paul Lisham,On Lisp的书.完整的PDF可以从我刚给出的链接下载.您也可以尝试Practical Common Lisp(也可在网上完全获得).

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


+1让waldo找到自己该死的自己:)
实用Common Lisp上的+1

3> Matthias Ben..:

您可能会发现本文有用:http://www.defmacro.org/ramblings/lisp.html

也就是说,给出Lisp功能的简短实用例子是非常非常困难的,因为它只是在非平凡的代码中才真正闪耀.当你的项目增长到一定规模时,你会欣赏Lisp的抽象设施,并且很高兴你一直在使用它们.另一方面,合理的短代码样本永远不会给你一个令人满意的演示,因为Lisp很棒,因为其他语言的预定义缩写在小例子中看起来比Lisp在管理特定于域的抽象中的灵活性更具吸引力.



4> Will Hartung..:

实际上,一个很好的实际例子是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是推动编程的好方法,即使在其他语言中也是如此.



5> Mikael Janss..:

我喜欢Common Lisp Object System(CLOS)和multimethods.

大多数(如果不是全部)面向对象编程语言都具有类和方法的基本概念.Python中的以下片段定义了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中让香蕉接受它.但是,它必须属于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等功能,可以让您快速轻松地实现结果,而无需跳过如此多的箍,您可以解决这个难以解决的问题,或者发现自己变成了袋鼠.


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

6> Nowhere man..:

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

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

Etvoilà!您刚刚使用新的基本结构扩展了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语言做这件事都留给读者练习......


为什么这么复杂?虽然可以定义(defmacro while(condition&body body)`(do()((not,condition)),@ body))...

7> Nelson..:

我发现这篇文章非常有趣:

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

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

Corfman发现很难将他的解决方案减少到少于142行的C++/STL.他对原因的分析是一个有趣的读物.


啊! 伤心.链接腐.

8> Sébastien Ro..:

我最喜欢Lisp(和Smalltalk)系统的是他们觉得活着.您可以在Lisp系统运行时轻松探测和修改它们.

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

另一件事是代码=列表等价使得代码和数据之间的边界非常薄.并且由于宏,很容易扩展语言并快速制作DSL.

例如,可以编写一个基本的HTML构建器,代码与生成的HTML输出非常接近:

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

=>


  
    The title
  
  
    

The Headline

Some text here

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


我必须把这个模糊不清的"感觉活着"的东西放在第二位.在(Common)Lisp或Smalltalk中使用其他语言以我难以表达的方式工作确实感觉不同(并且更优越).

9> Attila Lendv..:

了解如何使用XML模板扩展Common Lisp:cl-quasi-quote XML示例,项目页面,

(babel:octets-to-string
 (with-output-to-sequence (*html-stream*)
   
>>)) => "
"

这与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*
          #))
    (transform-quasi-quoted-string-to-quasi-quoted-binary
      (let ((*transformation*
               #))
        (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+)

作为参考,上面的两个大字节向量在转换为字符串时看起来像这样:

"

第二个:

"\">
 
    
  
"

它与其他Lisp结构(如宏和函数)结合得很好.现在,将其与JSP进行比较......



10> Mike Dunlave..:

我是20世纪70年代麻省理工学院的AI学生.像其他学生一样,我认为语言是最重要的.然而,Lisp是主要语言.这些是我仍然认为它非常适合的一些事情:

符号数学.编写表达式的符号区分和代数简化是容易和有益的.我仍然会这样做,即使我用它做任何事情.

定理证明.我不时地进行临时AI狂欢,就像试图证明插入排序是正确的一样.为此我需要进行符号操作,我通常会依赖于Lisp.

少量特定领域的语言.我知道Lisp并不是实用,但是如果我想尝试一点DSL而不必将所有内容都解压缩等等,那么Lisp宏可以让它变得简单.

像minimax游戏树搜索这样的小游戏算法可以用三行来完成.

想尝试lambda演算?在Lisp中很容易.

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

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


ITA软件是Kayak,Orbitz,Bing旅行,美国航空等旅游网站背后的引擎,它使用Lisp进行搜索引擎,数据处理以及一些网页渲染.这当然有资格作为"处理大量数据"的"足够性能".它因其"漂亮,可靠的UI"而与Java共享信誉.我不否认你断言Lisp对实验软件也很好.请参阅:http://www.paulgraham.com/carl.html
你说至少两次Lisp不实用.为什么不呢?Clojure不实用吗?你的实际标准是什么?
@SuperElectric:嗯,我印象深刻.感谢更新.

11> 小智..:

我喜欢这个来自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环境的强大功能.



12> Vatine..:

我喜欢的一件事是我可以在不丢失应用程序状态的情况下升级代码"运行时".这在某些情况下是有用的,但是当它有用时,将它放在那里(或者,在开发期间只需要很少的成本)比从头开始实现它要便宜得多.特别是因为这是"几乎没有"的成本.


这是开发过程中的杀手级功能!

13> William Kell..:

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

推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有