有人试图向我出售Lisp,作为一种超级强大的语言,可以做任何事情,然后一些.
是否有Lisp功能的实用代码示例?
(最好与用常规语言编码的等效逻辑一起使用.)
我喜欢宏.
这是从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代码(不是#define
cpp中的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的一个特性.
我能想到的最好的例子是Paul Lisham,On Lisp的书.完整的PDF可以从我刚给出的链接下载.您也可以尝试Practical Common Lisp(也可在网上完全获得).
我有很多不切实际的例子.我曾经在大约40行lisp中编写了一个程序,它可以解析自己,将其源代码作为lisp列表,对列表进行树遍历,并构建一个表达式,如果waldo标识符存在于源代码中或评估为to WALDO如果沃尔多不在场,那就没有了.返回的表达式是通过将对car/cdr的调用添加到已解析的原始源来构造的.我不知道如何用40行代码在其他语言中做到这一点.也许perl可以用更少的线来做到这一点.
您可能会发现本文有用:http://www.defmacro.org/ramblings/lisp.html
也就是说,给出Lisp功能的简短实用例子是非常非常困难的,因为它只是在非平凡的代码中才真正闪耀.当你的项目增长到一定规模时,你会欣赏Lisp的抽象设施,并且很高兴你一直在使用它们.另一方面,合理的短代码样本永远不会给你一个令人满意的演示,因为Lisp很棒,因为其他语言的预定义缩写在小例子中看起来比Lisp在管理特定于域的抽象中的灵活性更具吸引力.
实际上,一个很好的实际例子是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是推动编程的好方法,即使在其他语言中也是如此.
我喜欢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等功能,可以让您快速轻松地实现结果,而无需跳过如此多的箍,您可以解决这个难以解决的问题,或者发现自己变成了袋鼠.
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语言做这件事都留给读者练习......
我发现这篇文章非常有趣:
编程语言比较:Lisp vs C++
该文章的作者Brandon Corfman写了一篇研究,将Java,C++和Lisp中的解决方案与编程问题进行比较,然后在C++中编写自己的解决方案.基准解决方案是Peter Norvig的45行Lisp(2小时内编写).
Corfman发现很难将他的解决方案减少到少于142行的C++/STL.他对原因的分析是一个有趣的读物.
我最喜欢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代码中,自动缩进使代码看起来像输出,除了没有任何结束标记.
了解如何使用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..:你已经采取了看看这个为什么宏是强大和灵活的解释吗?不过其他语言没有例子,抱歉,但它可能会在宏上卖给你.
推荐阅读
如何解决《在Android上验证数字签名》经验,为你挑选了1个好方法。 ... [详细] 如何解决《hybrispopulatorsandconverter概念及其关系》经验,为你挑选了1个好方法。 ... [详细] 如何解决《获取Swift中UIPanGestureRecognizer的起点和终点》经验,为你挑选了1个好方法。 ... [详细] 如何解决《自动创建的用于xml反序列化的C#类不起作用》经验,为你挑选了2个好方法。 ... [详细] 如何解决《模拟类并在对方法的调用中进行断言的正确方法》经验,为你挑选了1个好方法。 ... [详细] 如何解决《如何强制C++从全局命名空间中选择一个函数?》经验,为你挑选了1个好方法。 ... [详细] 如何解决《在自定义.plist文件中使用用户定义的构建设置》经验,为你挑选了1个好方法。 ... [详细] 如何解决《通过传递带有列名的有序向量来动态排序dplyr中的列以进行选择》经验,为你挑选了2个好方法。 ... [详细] 如何解决《为什么@Ignore注释不起作用?》经验,为你挑选了1个好方法。 ... [详细] 如何解决《css3转换为模拟当前的开口》经验,为你挑选了0个好方法。 ... [详细] 如何解决《将64位十六进制转换为在PHP中浮动》经验,为你挑选了1个好方法。 ... [详细] 如何解决《如何使用ansible编写eclipse安装和配置脚本?》经验,为你挑选了1个好方法。 ... [详细] 如何解决《如何在node.js可读流中调用异步函数》经验,为你挑选了0个好方法。 ... [详细] 如何解决《具有非完全整数类型的结构和枚举》经验,为你挑选了1个好方法。 ... [详细] 如何解决《在JavaScript中拳击强制?》经验,为你挑选了1个好方法。 ... [详细] 如何解决《访问父iframe时,为什么我在MicrosoftEdge中获得"权限被拒绝"》经验,为你挑选了0个好方法。 ... [详细] 如何解决《如何启动手机互联网设置对话框?》经验,为你挑选了0个好方法。 ... [详细] 如何解决《如何在Magento2中安装语言包?》经验,为你挑选了1个好方法。 ... [详细] 如何解决《如何连接运行Android4.4的AndroidIPTV机顶盒进行USB调试》经验,为你挑选了1个好方法。 ... [详细] 如何解决《仅在C/C++模式下绑定密钥?》经验,为你挑选了1个好方法。 ... [详细]吐了个 "CAO" !Tags | 热门标签RankList | 热门文章
- 1有效地处理文本文件中的数据
- 2PUT的HTTP状态代码
- 3在rails视图中跳过迭代
- 4回归分析中的分类和序数特征数据表示?
- 5Matplotlib:其bbox中的中心文本
- 6如何在Xcode中调试ios的金属内核?
- 7反应原生的TextInput焦点样式
- 8containsTheSameElementsAs如何在specs2中工作
- 9在Delphi 10 Seattle上接收iOS推送通知
- 10在空内使用修剪
- 11如何跳过Perl中while循环中的迭代步骤?
- 12如何将其他参数传递给pyspark中用户定义的方法进行过滤方法?
- 13如何在SBT项目中列出活动插件?
- 14Android在Android UI中绘制圆角矩形
- 15如何创建未在类中指定的成员的对象?
- 16指针声明数组
- 17在ios9中,GCM注册尚未准备好使用auth凭据
- 18java中的immutable和final有什么区别?
- 19Open Layers 3获得Google Maps baselayer?
- 20没有类导航的Bootstrap scrollspy
DevBox开发工具箱 | 专业的在线开发工具网站 京公网安备 11010802040832号 | 京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有