有人正试图出售Lisp的我,作为一个超级强大的语言,都不能做到的一切,然后一些。
有没有Lisp的权力的实际代码的例子吗?
(优选地沿着等效逻辑以规则的语言编码。)
有人正试图出售Lisp的我,作为一个超级强大的语言,都不能做到的一切,然后一些。
有没有Lisp的权力的实际代码的例子吗?
(优选地沿着等效逻辑以规则的语言编码。)
我喜欢宏。
下面的代码将东西拿出从LDAP人的属性。 我刚好有一个代码周围地势和fiigured它会成为别人有用。
有些人混淆了宏的设想运行点球,所以我已经添加在末澄清事情的尝试。
(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形式之外。 注意绑定的形式 - 他们都非常相似,不同的只是在LDAP实体和名称(“局部变量”)的属性值进行绑定。 有用的,但有点冗长和重复包含。
现在,那岂不是很好,如果我们没有有所有的重复? 一个常见的成语是为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代码(不是#define
生成Lisp代码的CPP绒毛),前其他的代码被编译。 宏可以使用任何实际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-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
,我们可以既是)使用更合适的动词,如果我们没有一个虚假的分支,和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中我喜欢的功能之一。
我能想到的,是广泛使用的最好的例子就是书保罗·格雷厄姆, Lisp上 。 完整的PDF可以从我刚才给的链接下载。 您也可以尝试实践的Common Lisp (还有在网络上完全可用)。
我有很多不切实际的例子。 我曾经在约40口齿不清这可以解析本身,把其源作为口齿不清列表,做列表的树遍历和构建评价WALDO一个表达式,如果金都标识源存在或评估,以线写了一个程序零如果沃尔不存在。 返回的表达通过添加调用车/ CDR到已解析的原始源构成。 我不知道如何在代码40行要做到这一点其他语言。 也许Perl可以在更少行做到这一点。
您可能会发现这篇文章有帮助: http://www.defmacro.org/ramblings/lisp.html
这就是说,这是非常,非常努力给Lisp的权力短,实际的例子,因为它真的只闪耀在不平凡的代码。 当你的项目开始发展到一定规模时,你会明白Lisp的抽象设施,很高兴,你一直在使用它们。 合理的短代码示例,而另一方面,也永远不可能给你的是什么使Lisp的伟大的,因为其它语言的预定义的缩写看起来比Lisp的管理具体领域抽象的灵活性小例子更具吸引力的一个令人满意的演示。
事实上,一个好的实际的例子是Lisp的LOOP宏。
http://www.ai.sri.com/pkarp/loop.html
循环宏很简单,就是 - 一个Lisp宏。 然而,它基本上定义一个小型循环DSL(领域特定语言)。
当您浏览通过小教程,你可以看到(即使是新手),它是很难知道什么代码的一部分,是河套宏的一部分,而这是“正常”的Lisp。
这就是的Lisp表现的关键部件之一,新的代码真的不能从系统中区别开来。
虽然,比方说,Java中,你可能不会(一览)能够知道来自哪个计划的一部分,从标准的Java库与你自己的代码,甚至是第三方库,你就知道代码的哪一部分是Java语言,而不是简单的类方法调用。 当然,这一切都在“Java语言”,但作为程序员,你是仅限于表达自己的应用程序的类和方法(现在,注释)的组合。 而在Lisp中,所有的一切是待价而沽。
考虑常见的SQL接口,Common Lisp的连接到SQL。 在这里, http://clsql.b9.com/manual/loop-tuples.html ,他们表现出CL环路宏如何延长使SQL结合了“一等公民”。
您也可以观察结构,如“[选择[第一名称] [最后名称]:从[职员]:为了按[上次名称]”。 这是CL-SQL包的一部分并作为“读取器宏”实现。
见,Lisp中,你不仅可以让宏来创建新的结构,如数据结构,控制结构等,但你甚至可以通过一个阅读宏观改变语言的语法。 在这里,他们正在使用阅读器的宏(在的情况下,“[”符号)中下降到一个SQL模式,使SQL工作像嵌入式SQL,而不是在许多其他语言,如刚刚原始字符串。
应用程序开发人员,我们的任务是我们的流程和结构转换中的形式,处理器可以理解的。 这意味着,我们不可避免地要“谈下来”的计算机语言,因为它“不理解”我们。
Common Lisp是为数不多的环境中,我们不仅可以建立我们从上而下的应用程序之一,但在这里我们可以解除语言和环境,以满足我们一半。 我们可以在两端进行编码。
心灵,优雅因为这可能是,它没有灵丹妙药。 显然,是影响语言和环境的选择等因素。 但它一定是值得学习和玩耍。 我认为学习Lisp是来为自己的节目,甚至在其他语言的好方法。
我喜欢的Common Lisp对象系统 (CLOS)和多方法。
大多数情况下,如果不是全部,面向对象的编程语言都有的类和方法的基本概念。 在下面的代码段的Python定义的类PeelingTool和蔬菜(类似于访问者模式的东西):
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的杀手锏,但宏是一个我爱particularily,因为它们之间是什么语言定义,我定义什么了不是一个真正的屏障。 例如,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编程语言就留给读者做练习...
我发现这篇文章很有意思:
编程语言比较:Lisp的VS C ++
该文章的作者,布兰登Corfman,写的是,在Java和C ++的解决方案进行比较研究和Lisp的规划问题,然后写在C ++自己的解决方案。 基准溶液是彼得·诺维格的45行的Lisp(写在2小时)的。
Corfman发现它难以减少他的溶液,以小于142线C ++ / STL的。 他的原因,是一个有趣的阅读分析。
我最喜欢的Lisp(和事Smalltalk的 )系统,是他们觉得我还活着。 您可以轻松地探针,而他们正在运行的修改Lisp的系统。
如果这听起来很神秘,启动Emacs的 ,并键入一些Lisp代码。 键入CMx
就万事大吉了! 你只改变了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代码,自动缩进使代码看起来像输出,除了没有任何结束标记。
我喜欢这个宏例如,从http://common-lisp.net/cgi-bin/viewcvs.cgi/cl-selenium/?root=cl-selenium这是一个Common Lisp的结合硒(网络浏览器测试框架),但不是映射的每个方法,在编译时读取硒自己的API定义XML文档,并生成使用宏映射代码。 你可以在这里看到生成的API:common-lisp.net/project/cl-selenium/api/selenium-package/index.html
这本质上是推动宏与外部数据,这恰好是在这种情况下的XML文档,但可能是复杂的,从数据库或网络阅读。 这是具有在编译时提供给您整个Lisp的环境的能力。
看你怎么可以用XML模板扩展的Common Lisp: CL-准报价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=\"&foo&bar\">
<someRandomElement>
<someOther/>
</someRandomElement>
</div>"
这基本上是同样的事情Lisp的反引号阅读器(这是清单准报价),但它也适用于其他各种东西,如XML(安装在特殊的<>语法),JavaScript的(安装在`JS-在线)等。
要清楚,这是在用户库中实现! 和它编译静态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的 ...
我在麻省理工学院在20世纪70年代的AI学生。 像其他的学生,我认为语言是极为重要的。 尽管如此,Lisp语言是主要语言。 这些都是一些东西我仍然认为这是相当不错的:
符号数学。 这是很容易和有启发写表达式的符号微分和代数简化。 我还是做那些,即使我做他们在C-什么。
定理证明。 时不时&然后我去上的临时AI狂欢,就像试图证明插入排序是正确的。 为此,我需要做象征性的操作,我通常依傍Lisp的。
小领域特定语言。 我知道Lisp是不是真的实用,但如果我想尝试一点DSL ,而不必让所有包裹在解析等,Lisp的宏可以很容易。
小游戏算法,如极小博弈树搜索可以像三条线来完成。
主要是什么呢Lisp语言对我来说是脑力锻炼。 然后,我可以携带在成更实用的语言。
演算,什么也开始在20世纪70年代的PS说起,在同一AI millieu,是OO开始侵入每个人的大脑,在某种程度上,它是什么样的兴趣似乎在什么是良好的 ,以排挤了很大的兴趣。 即在机器学习,自然语言,视力,解决问题的工作,所有种类的跑到房间的后面,而类,消息类,多态性,等走到前面。
有一件事我喜欢的是,我可以升级代码“运行时”,而不会丢失应用程序状态的事实。 这只是在某些情况下是有用的事情,但是当它是有用的,有它已经存在(或者,只有在开发过程中以最低的成本)是远远超过不必从头开始实现它便宜。 特别是因为这是以“不来几乎没有”成本。
你已经采取了看看这个为什么宏是强大和灵活的解释吗? 在其他语言中没有例子虽然遗憾,但它可能会卖给你的宏。
@标记,
虽然有一定的道理,你说的话,我相信它并不总是直线前进。
程序员和一般人不总是评估所有的可能性,并决定切换语言的时间。 往往就是这么决定的管理者,或者教第一语言学校......和程序员从来没有需要投入的时间足够量达到一定水平了,他们可以决定这种语言为我节省了比语言更多的时间。
另外,你不得不承认,有巨大的商业实体,如微软或Sun的支持语言将始终有一个优势,在市场上比没有这种支持的语言。
为了回答原来的问题,保罗·格雷厄姆试图给出一个例子在这里 ,尽管我承认它并不一定实用 ,因为我想:-)
给我印象最深的一个具体的事情是写自己的面向对象编程的扩展,如果你碰巧不喜欢所包含的CLOS的能力。
其中之一是石榴石 ,一个在保罗·格雷厄姆的Lisp上 。
还有一个叫包尖叫 ,让不确定的编程(我还没有计算)。
任何允许您更改它支持不同的编程范式语言必须是灵活的。
您可能会发现这个帖子由埃里克·诺曼德很有帮助。 他介绍了如何作为一个代码库的增长,Lisp的帮助,让你构建语言到你的应用程序。 虽然这往往需要额外的努力在早期,它以后给你一个很大的优势。
一个简单的事实,这是一个多范式语言使得它非常非常灵活。
约翰·斯特豪特在1994年就Lisp的这个有趣的现象:
语言的设计者喜欢辩论为什么这种语言或语言必须是好还是坏先验的,但没有这些论点确实问题很多。 最终,当用户用脚投票的所有语言问题安顿下来。
如果[语言]使人们更富有成效那么他们将使用它; 当其他语言走来是更好(或者,如果它是在这里的话),那么人们将切换到该语言。 这是法律,这是件好事。 该法对我说,计划(或任何其他的Lisp方言)可能不是“正确”的语言:太多人用脚投票,在过去30年。
http://www.vanderburg.org/OldPages/Tcl/war/0009.html