LISP宏可以走多远?
我已经读了很多书,LISP可以动态地重新定义语法,大概是使用宏。我很好奇这实际上能走多远?我们是否可以重新定义语言结构,以至于边界变成了另一种语言的编译器?例如,我们是否可以将LISP的功能性质更改为更面向对象的语法和语义,例如说使语法更接近Ruby之类的东西?
特别是,是否可以使用宏摆脱括号地狱?我已经学到了足够多的(Emacs-)LISP,可以使用自己的微功能自定义Emacs,但是我很好奇宏可以在自定义语言中走多远。
解决方案
回答
如果我们希望Lisp看起来像Ruby,请使用Ruby。
可能以非常轻率的方式使用Ruby(和Python),这是他们如此迅速获得认可的主要原因之一。
回答
@sparkes
有时LISP是明确的语言选择,即Emacs扩展。我敢肯定,如果愿意的话,我可以使用Ruby来扩展Emacs,但是Emacs旨在通过LISP进行扩展,因此在这种情况下使用它似乎是有意义的。
回答
这是一个棘手的问题。由于Lisp在结构上已经非常接近解析树,因此大量宏与在解析器生成器中实现自己的迷你语言之间的区别并不十分清楚。但是,除了打开和关闭括号外,我们可能很容易以类似于lisp的内容结束文字。
回答
常规宏对对象列表进行操作。最常见的是,这些对象是其他列表(因此形成树)和符号,但它们可以是其他对象,例如字符串,哈希表,用户定义的对象等。这些结构称为s-exps。
因此,当我们加载源文件时,Lisp编译器将解析文本并生成s-exps。宏对这些进行操作。这很有效,并且是在s-exp精神内扩展语言的绝妙方法。
此外,上述解析过程可以通过"阅读器宏"扩展,该宏使我们可以自定义编译器将文本转换为s-exp的方式。但是,我建议我们采用Lisp的语法,而不是将其屈从于其他语法。
当我们提到Lisp的"功能性质"和Ruby的"面向对象的语法"时,我们会感到有点困惑。我不确定应该是什么"面向对象的语法",但是Lisp是一种多范式语言,它非常好地支持面向对象的编程。
顺便说一句,当我说Lisp时,我的意思是Common Lisp。
我建议我们放下偏见,并诚实地对待Lisp。
回答
我不是Lisp专家,哎呀,我什至都不是Lisp程序员,但是在对该语言进行了一些试验之后,我得出的结论是,一段时间后括号开始变得"不可见",我们开始将代码视为你想成为。我们开始更多地关注通过s-exprs和宏创建的语法结构,而不是列表和括号文本的词汇形式。
如果我们利用好的编辑器可以帮助缩进和语法着色(请尝试将括号设置为与背景非常相似的颜色),则尤其如此。
我们可能无法完全替换该语言并获得" Ruby"语法,但是我们不需要它。得益于语言的灵活性,我们可以随意使用一种方言,这对我们来说意味着什么,如果我们愿意,我们可以遵循" Ruby编程风格"。
我知道这只是一个实证观察,但是当我意识到这一点时,我想我曾经历过Lisp的那些启蒙时刻之一。
回答
是的,我们可以从根本上更改语法,甚至可以转义"圆括号"。为此,我们将需要定义新的阅读器语法。查看阅读器宏。
但是,我确实怀疑要达到Lisp专业知识的水平来编写此类宏,我们将需要使自己沉浸在该语言中,以致我们将不再考虑括号" hell"。 IE。当我们知道如何避免它们时,我们将开始接受它们为好东西。
回答
这是一个非常好的问题。
我认为这很细微,但绝对可以回答:
宏不会停留在s表达式中。有关使用关键字(符号)编写的非常复杂的语言,请参见LOOP宏。因此,尽管我们可以使用括号来开始和结束循环,但循环内部具有自己的语法。
例子:
(loop for x from 0 below 100 when (even x) collect x)
话虽如此,大多数简单的宏仅使用s表达式。而且我们会被它们"卡住"。
但是,像塞尔吉奥(Sergio)回答的那样,S表达式开始感觉正确。语法变得混乱,我们开始在语法树中进行编码。
至于阅读器宏,是的,可以想象这样写:
#R{ ruby.code.goes.here }
但是我们需要编写自己的Ruby语法解析器。
我们还可以使用编译为现有Lisp构造的宏来模仿某些Ruby构造(例如块)。
#B(some lisp (code goes here))
将转化为
(lambda () (some lisp (code goes here)))
请参阅此页面以了解操作方法。
回答
括号地狱?我在以下位置看不到括号:
(function toto)
比:
function(toto);
而在
(if tata (toto) (titi) (tutu))
不超过:
if (tata) toto(); else { titi(); tutu(); }
我看到的方括号和';'少了尽管。
回答
是的,我们可以重新定义语法,以便Lisp成为编译器。我们可以使用"阅读器宏"来执行此操作,这可能与我们可能想到的普通"编译器宏"不同。
Common Lisp具有内置功能,可以为阅读器和阅读器宏定义新语法以处理该语法。该处理在读取时(在编译或者评估时间之前)完成。要了解有关在Common Lisp中定义读取器宏的更多信息,请参见Common Lisp Hyperspec-我们需要阅读Ch。 2,"语法"和Ch。 23,"阅读器"。 (我相信Scheme具有相同的功能,但是我并不熟悉它-请参阅Arc编程语言的Scheme来源)。
作为一个简单的示例,让我们假设我们希望Lisp使用花括号而不是括号。这需要类似以下阅读器定义的内容:
;; { and } become list delimiters, along with ( and ). (set-syntax-from-char #\{ #\( ) (defun lcurly-brace-reader (stream inchar) ; this was way too easy to do. (declare (ignore inchar)) (read-delimited-list #\} stream t)) (set-macro-character #\{ #'lcurly-brace-reader) (set-macro-character #\} (get-macro-character #\) )) (set-syntax-from-char #\} #\) ) ;; un-lisp -- make parens meaningless (set-syntax-from-char #\) #\] ) ; ( and ) become normal braces (set-syntax-from-char #\( #\[ )
我们是在告诉Lisp,{就像一个(而}就像一个)。然后,我们创建一个函数(lcurly-brace-reader
),当阅读器看到{时,阅读器就会调用该函数,并使用set-macro-character
将该函数分配给{。然后,我们告诉Lisp(和)像和。
我们可能要做的其他事情包括,例如,创建新的字符串语法或者使用[和]包围中的符号并将其处理为S表达式。
我们还可以远远超出此范围,使用我们自己的宏字符重新定义整个语法,这些宏字符将触发阅读器中的操作,因此,真正的限制就是了。这只是Paul Graham和其他人不断说Lisp是编写编译器的好语言的原因之一。
回答
一遍又一遍,Lisp的新手想"摆脱所有括号"。它持续了几个星期。在普通的S表达式解析器之上构建严格的通用编程语法的项目无处可寻,因为程序员总是喜欢上我们当前认为的"括号地狱"。这需要一点时间来适应,但不多!一旦我们习惯了它,就可以真正体会到默认语法的可塑性,回到那些只有一种表达任何特定编程结构的方法的语言。
话虽这么说,Lisp是构建特定领域语言的出色基础。与XML一样好,甚至更好。
祝你好运!
回答
请参阅以下示例,说明读取器宏如何通过复杂的任务(例如XML模板)扩展lisp读取器:
http://common-lisp.net/project/cl-quasi-quote/present-class.html
该用户库在编译时将XML的静态部分编译为UTF-8编码的文字字节数组,这些数组准备以写入顺序的形式写入网络流。而且它们可用于普通的lisp宏中,它们是正交的...逗号字符的位置会影响哪些部分是恒定的,哪些部分应在运行时进行评估。
有关更多详细信息,请访问:http://common-lisp.net/project/cl-quasi-quote/
另一个用于Common Lisp语法扩展的项目:http://common-lisp.net/project/cl-syntax-sugar/
回答
我们要问的问题有点像问如何成为专家巧克力专家,这样我们就可以从自己喜欢的巧克力蛋糕中去除所有地狱般的棕色东西。
回答
宏的用途之一令我大吃一惊,是针对数据库的SQL请求的编译时验证。
一旦意识到在编译时手边有完整的语言,它就会打开有趣的新视角。这也意味着我们可以采用有趣的新方式(例如,无法进行可编译的渲染编译,这很容易变成调试的噩梦),使自己陷入困境。
回答
我见过的Lisp宏的最佳解释是在
https://www.youtube.com/watch?v=4NO83wZVT0A
从大约55分钟开始。这是彼得·塞贝尔(Peter Seibel)演讲的视频,"彼得·塞普尔(Practical Common Lisp)"是一本最好的Lisp教科书。
Lisp宏的动机通常很难解释,因为它们太长了,无法在简单的教程中介绍,因此它们确实会出现。彼得提出了一个很好的例子。我们可以完全掌握它,并且可以很好地正确使用Lisp宏。
我们问:"能否将LISP的功能性质更改为更面向对象的语法和语义"。答案是肯定的。实际上,Lisp最初根本没有任何面向对象的编程,这并不奇怪,因为Lisp早在面向对象编程之前就已经存在!但是当我们在1978年首次了解OOP时,我们能够使用宏将其轻松地添加到Lisp中。最终,开发了Common Lisp对象系统(CLOS),这是一个非常强大的面向对象的编程系统,非常适合Lisp。整个东西都可以作为扩展加载-什么都没有内置!所有这些都由宏完成。
Lisp具有完全不同的功能,称为"阅读器宏",可用于扩展语言的表面语法。使用阅读器宏,可以使子语言具有类似C或者类似Ruby的语法。他们在内部将文本转换为Lisp。大多数真正的Lisp程序员并没有广泛使用它们,这主要是因为很难扩展交互式开发环境来理解新语法。例如,Emacs缩进命令将被新语法混淆。但是,如果我们精力充沛,Emacs也是可扩展的,我们可以教它有关新词法语法的知识。