阅读Paul Graham关于编程语言的文章,你可能会认为Lisp宏是唯一的选择。作为一个忙碌的开发人员,在其他平台上工作,我还没有使用Lisp宏的特权。作为一个想要了解热门话题的人,请解释一下是什么让这个功能如此强大。
请将这一点与我从Python、Java、c#或C开发世界中理解的东西联系起来。
阅读Paul Graham关于编程语言的文章,你可能会认为Lisp宏是唯一的选择。作为一个忙碌的开发人员,在其他平台上工作,我还没有使用Lisp宏的特权。作为一个想要了解热门话题的人,请解释一下是什么让这个功能如此强大。
请将这一点与我从Python、Java、c#或C开发世界中理解的东西联系起来。
当前回答
Lisp宏代表了几乎在任何大型编程项目中都会出现的一种模式。最终,在一个大的程序中,你会有一段代码,你会意识到,如果你写一个程序,把源代码输出为文本,然后你就可以粘贴进去,这会更简单,更不容易出错。
在Python中,对象有两个方法__repr__和__str__。__str__只是人类可读的表示。__repr__返回一个有效的Python代码表示,也就是说,可以作为有效的Python输入解释器。通过这种方式,您可以创建一些小的Python代码片段,生成可以粘贴到实际源代码中的有效代码。
在Lisp中,整个过程由宏系统形式化。当然,它允许您为语法创建扩展并执行各种奇特的事情,但它的实际用处可以从上面总结出来。当然,Lisp宏系统允许您使用整个语言的全部功能来操作这些“片段”是有帮助的。
其他回答
我不确定我能给每个人的(优秀的)帖子添加一些见解,但是……
Lisp宏工作得很好,因为Lisp语法的本质。
Lisp是一种非常规则的语言(把所有东西都想象成一个列表);宏使您能够将数据和代码视为相同的(不需要字符串解析或其他技巧来修改lisp表达式)。将这两个特性结合起来,就有了一种非常简洁的方式来修改代码。
编辑:我想说的是Lisp是同构的,这意味着Lisp程序的数据结构是用Lisp本身编写的。
因此,您最终可以在语言之上创建自己的代码生成器,使用语言本身的所有功能(例如。在Java中,你必须破解字节码编织的方法,尽管一些框架(如AspectJ)允许你使用不同的方法来做到这一点,但它基本上是一种破解)。
在实践中,使用宏可以在lisp的基础上构建自己的迷你语言,而不需要学习其他语言或工具,并使用语言本身的全部功能。
Lisp宏允许您决定何时(如果有的话)对任何部分或表达式求值。举个简单的例子,想想C语言:
expr1 && expr2 && expr3 ...
它说的是:计算expr1,并且,如果它是正确的,计算expr2,等等。
现在试着把这个&&变成一个函数…没错,你不能。像这样调用:
and(expr1, expr2, expr3)
将在产生答案之前评估所有三个表达式,而不管expr1是否为假!
使用lisp宏,你可以编写如下代码:
(defmacro && (expr1 &rest exprs)
`(if ,expr1 ;` Warning: I have not tested
(&& ,@exprs) ; this and might be wrong!
nil))
现在你有一个&&,你可以像函数一样调用它,它不会计算你传递给它的任何表单,除非它们都为真。
要了解这是如何有用的,请进行对比:
(&& (very-cheap-operation)
(very-expensive-operation)
(operation-with-serious-side-effects))
and:
and(very_cheap_operation(),
very_expensive_operation(),
operation_with_serious_side_effects());
你可以用宏做的其他事情是创建新的关键字和/或迷你语言(例如,查看(loop…)宏),将其他语言集成到lisp中,例如,你可以编写一个宏,让你这样说:
(setvar *rows* (sql select count(*)
from some-table
where column1 = "Yes"
and column2 like "some%string%")
这还没有涉及到Reader宏。
希望这能有所帮助。
While the above all explains what macros are and even have cool examples, I think the key difference between a macro and a normal function is that LISP evaluates all the parameters first before calling the function. With a macro it's the reverse, LISP passes the parameters unevaluated to the macro. For example, if you pass (+ 1 2) to a function, the function will receive the value 3. If you pass this to a macro, it will receive a List( + 1 2). This can be used to do all kinds of incredibly useful stuff.
Adding a new control structure, e.g. loop or the deconstruction of a list Measure the time it takes to execute a function passed in. With a function the parameter would be evaluated before control is passed to the function. With the macro, you can splice your code between the start and stop of your stopwatch. The below has the exact same code in a macro and a function and the output is very different. Note: This is a contrived example and the implementation was chosen so that it is identical to better highlight the difference. (defmacro working-timer (b) (let ( (start (get-universal-time)) (result (eval b))) ;; not splicing here to keep stuff simple ((- (get-universal-time) start)))) (defun my-broken-timer (b) (let ( (start (get-universal-time)) (result (eval b))) ;; doesn't even need eval ((- (get-universal-time) start)))) (working-timer (sleep 10)) => 10 (broken-timer (sleep 10)) => 0
In python you have decorators, you basically have a function that takes another function as input. You can do what ever you want: call the function, do something else, wrap the function call in a resource acquire release, etc. but you don't get to peek inside that function. Say we wanted to make it more powerful, say your decorator received the code of the function as a list then you could not only execute the function as is but you can now execute parts of it, reorder lines of the function etc.
想想在C或c++中可以用宏和模板做什么。它们是管理重复代码的非常有用的工具,但它们在相当严重的方面受到限制。
有限的宏/模板语法限制了它们的使用。例如,不能编写扩展为类或函数以外内容的模板。宏和模板不容易维护内部数据。 C和c++复杂且不规则的语法使得编写非常通用的宏非常困难。
Lisp和Lisp宏解决了这些问题。
Lisp宏是用Lisp编写的。您拥有Lisp的全部功能来编写宏。 Lisp有一个非常规则的语法。
与任何精通c++的人交谈,问他们花了多长时间来学习模板元编程所需的所有模板。或者是《现代c++设计》等(优秀)书籍中的所有疯狂技巧,尽管语言已经标准化了10年,但这些技巧仍然很难调试,而且(在实践中)无法在真实的编译器之间移植。如果用于元编程的语言与用于编程的语言相同,那么所有这些问题都消失了!