在Lisp中,任何程序的代码实际上都是有效的数据结构。例如,这会将一个和两个加在一起,但它也是三个项目的列表。
(+ 1 2)
它提供了什么好处?是什么让你在其他语言中做到不可能和/或不那么优雅?
答案 0 :(得分:18)
为了使代码表示更加清晰,请考虑在中每个语言代码都是数据:您需要的只是字符串。 (也许还有一些文件操作。)考虑如何帮助你模仿拥有宏系统的Lisp好处是一种很好的启蒙方式。如果你试图实现这样的宏系统,那就更好了。您将遇到结构化表示与字符串平坦性的优势,首先需要运行转换并定义“语法挂钩”以告诉您应用它们的位置等等。
但是你会在所有这些中看到的主要内容是宏本质上是一个方便的编译钩子工具 - 那些挂在新创建的关键字上的工具。因此,真正需要的唯一方法是让用户代码与编译器代码交互。扁平字符串是一种方法,但它们提供的信息很少,以至于宏编写器的任务是从头开始实现解析器。另一方面,您可以暴露一些内部编译器结构,如预解析的AST树,但那些往往暴露太多信息以方便和这意味着编译器需要以某种方式能够解析您打算实现的新语法扩展。 S表达式是后者的一个很好的解决方案:编译器可以解析任何东西,因为语法是统一的。它们也是前者的解决方案,因为它们是简单的结构,并且有很多语言支持,可以将它们分开,并以新的方式重新组合它们。
但当然这不是故事的结尾。例如,比较CL中的普通符号宏和Scheme实现中的卫生宏是很有趣的:这些宏通常通过向表示的数据添加更多信息来实现,这意味着您可以使用这些宏系统做更多的事情。 (您也可以使用CL宏执行更多操作,因为额外的信息也可用,但它不是将其作为语法表示的一部分,而是作为宏的额外环境参数传递。)
答案 1 :(得分:8)
我最喜欢的例子......在大学里,我的一些朋友正在用Lisp编写一个编译器。因此,包括解析树在内的所有数据结构都是lisp s表达式。在实施代码生成阶段时,他们只是执行了他们的解析树。
答案 2 :(得分:3)
Lisp是为处理各种符号数据而开发的。事实证明,人们也可以将任何Lisp程序视为符号数据。因此,您可以将Lisp的操作功能应用于自身。
如果你想用程序计算(创建新程序,将程序编译成机器代码,将程序从Lisp翻译成其他语言),基本上有三种选择:
使用字符串。这一直都是繁琐的解析和解析字符串。
使用解析树。有用但很复杂。
使用Lisp中的符号表达式。程序被预先准备好熟悉的数据结构(列表,符号,字符串,数字......),操作例程用通常的语言提供的功能编写。
那么你用Lisp得到什么?编写操作其他程序的程序的一种相对简单的方法。从宏到编译器,有很多例子。您还可以轻松地将语言嵌入到Lisp中。
答案 3 :(得分:2)
它允许您编写简单地将一个列表树转换为另一个列的宏。简单(Scheme)示例:
(define-syntax and
(syntax-rules ()
((and) #t)
((and thing) thing)
((and thing rest ...) (if thing (and rest ...) #f))))
注意宏调用如何简单地与(and)
,(and thing)
和(and thing rest ...)
子句匹配(取决于调用的arity),并进行适当处理。
对于其他语言,宏必须处理正在转换的代码的某种内部AST - 否则就不会有一种以程序化格式“看到”代码的简单方法 - 而且会增加写宏的摩擦力。
在Lisp程序中,宏通常被频繁使用,正是由于写入它们的摩擦力低。