我听说过Lisp让你重新定义语言本身,我试图研究它,但是在任何地方都没有明确的解释。有没有人有一个简单的例子?
答案 0 :(得分:86)
答案 1 :(得分:14)
我要说的是,在定义新语法时,Scheme与Common Lisp不同。它允许您使用define-syntax
定义模板,无论它们在何处使用,都会应用于您的源代码。它们看起来就像函数一样,只有它们在编译时运行并转换AST。
以下是let
如何根据lambda
定义的示例。 let
行是要匹配的模式,lambda
行是生成的代码模板。
(define-syntax let
(syntax-rules ()
[(let ([var expr] ...) body1 body2 ...)
((lambda (var ...) body1 body2 ...) expr ...)]))
请注意,这与文本替换不同。您实际上可以重新定义lambda
,let
的上述定义仍然有效,因为它在定义lambda
的环境中使用let
的定义。基本上,它像宏一样强大,但功能很干净。
答案 2 :(得分:4)
宏是说这个的常见原因。这个想法是因为代码只是一个数据结构(树或多或少),你可以编写程序来生成这个数据结构。因此,您所知道的关于编写生成和操作数据结构的程序的所有内容都会增加您的表达能力。
宏并不是对语言的完全重新定义,至少据我所知(我实际上是一个Schemer;我可能是错的),因为有一个限制。宏只能占用代码的单个子树,并生成一个子树来替换它。因此,你不能编写整个程序转换宏,就像那样酷。
然而,他们所处的宏仍然可以做很多事情 - 绝对比任何其他语言更能让你做到。如果您正在使用静态编译,那么完成整个程序转换并不困难,因此限制就不那么重要了。
答案 3 :(得分:3)
对于“计算机程序的结构和解释”第4-5章的引用是我在答案中遗漏的内容(link)。
这些章节指导您在Lisp中构建Lisp评估程序。我喜欢阅读,因为它不仅展示了如何在新的评估器中重新定义Lisp,还让您了解Lisp编程语言的规范。
答案 4 :(得分:2)
这个答案特别涉及Common Lisp(以下称CL),尽管部分答案可能适用于lisp系列中的其他语言。
由于CL使用S表达式并且(大多数)看起来像一系列函数应用程序,因此内置函数和用户代码之间没有明显的区别。主要区别在于“语言提供的东西”在编码环境中的特定包中可用。
稍微小心点,替换代码并使用它们并不难。
现在,“普通”阅读器(读取源代码并将其转换为内部表示法的部分)要求源代码采用相当特定的格式(带括号的S表达式),但读者是由被调用的东西驱动的“读表”这些可以由开发人员创建和修改,也可以改变源代码的外观。
这两件事至少应该提供一些理由,说明为什么Common Lisp可以被认为是一种可重新编程的编程语言。我手边没有一个简单的例子,但我确实部分实现了Common Lisp到瑞典语的翻译(几年前创建于4月1日)。
答案 5 :(得分:2)
从外面看,......
我一直认为这是因为Lisp在其核心提供了这样的基本原子逻辑运算符,可以从基本组件构建任何逻辑进程(并且已经构建并作为工具集和加载项提供)。 p>
它可以重新定义自己,因为它的基本定义是如此具有可塑性,以至于它可以采取任何形式,并且不会假设/假定结构中存在任何形式。
作为一个比喻,如果你只有有机化合物,你做有机化学,如果你只有金属氧化物,你做冶金,但如果你只有元素,你可以做任何事情,但你有额外的初步步骤来完成....其他人已经为你做过....
我想......
答案 6 :(得分:2)
http://www.cs.colorado.edu/~ralex/papers/PDF/X-expressions.pdf
的酷示例读取器宏定义X表达式与S表达式共存,例如,
? (cx <circle cx="62" cy="135" r="20"/>)
62
普通Lisp http://www.AgentSheets.com/lisp/XMLisp/XMLisp.lisp ...
(eval-when (:compile-toplevel :load-toplevel :execute)
(when (and (not (boundp '*Non-XMLISP-Readtable*)) (get-macro-character #\<))
(warn "~%XMLisp: The current *readtable* already contains a #/< reader function: ~A" (get-macro-character #\<))))
...当然,XML解析器并不是那么简单,但将其挂钩到lisp阅读器中。