为什么Common Lisp中存在函数/宏二分法?
允许同名表示宏(在编译/ eval中的函数位置找到优先级)和函数(例如可用)的逻辑问题是什么mapcar
?
例如,second
既定义为宏又作为函数允许使用
(setf (second x) 42)
Run Code Online (Sandbox Code Playgroud)
和
(mapcar #'second L)
Run Code Online (Sandbox Code Playgroud)
无需创建任何setf
技巧.
当然很明显,宏可以做的不仅仅是函数,所以类比不能完整(我当然不认为每个宏都是一个函数)但是为什么要禁止它,因为它可以共享一个命名空间可能有用吗?
我希望我不会冒犯任何人,但我真的没有找到"为什么这样做?" 回应真的很相关...我在寻找为什么这是一个坏主意.施加任意限制,因为没有很好的用途是IMO有点傲慢(有点假设完美的远见).
或允许它有实际问题吗?
Rai*_*wig 13
宏和函数是两个非常不同的东西:
宏正在使用源(!!!)代码并生成新的源(!!!)代码
函数是参数化的代码块.
现在我们可以从几个角度来看这个,例如:
a)我们如何设计一种语言,其中功能和宏清晰可辨,并且在我们的源代码中看起来不同,所以我们(人类)可以很容易地看到什么是什么?
要么
b)我们如何以最有用的结果和控制其行为的最有用规则来混合宏和函数?对于用户来说,使用宏或函数应该没有区别.
我们真的需要说服自己b)是要走的路,我们想使用一种语言,其中宏和函数的使用看起来是一样的,并且按照类似的原则工作.乘船和汽车.他们看起来不同,他们的用例大多不同,他们运送人 - 我们现在应该确保他们的交通规则大部分是相同的,我们应该使他们不同,还是我们应该为他们的特殊用途设计规则?
对于函数,我们有问题,如:定义一个函数,函数,函数的生命时间范围,绕过函数,返回函数,调用函数的功能,功能扩展阴影,消除定义的函数的函数,编译和解释,...
如果我们要使宏看起来与函数大致相似,我们需要解决它们的大部分或全部问题.
在您的示例中,您提到了一个SETF表单.SETF是一个宏,它在宏扩展时分析封闭的表单并为setter生成代码.它与SECOND
宏观无关.有SECOND
被宏不会在所有帮助过这种情况.
那么,问题的例子是什么?
(defmacro foo (a b)
(if (and (numberp b) (zerop b))
a
`(- ,a ,b)))
(defun bar (x list)
(mapcar #'foo (list x x x x) '(1 2 3 4)))
Run Code Online (Sandbox Code Playgroud)
那该怎么办?直观地看起来很简单:映射FOO
列表.但事实并非如此.当Common Lisp被设计出来时,我猜,不清楚应该做什么以及它应该如何工作.如果FOO
是一个函数,那么很明显:Common Lisp将来自Scheme的思想置于词法范围的第一类函数之后并将其集成到语言中.
但是一流的宏?在Common Lisp的设计之后,一系列研究进入了这个问题并进行了调查.但是在Common Lisp的设计时,没有广泛使用的一流宏,也没有设计方法的经验.Common Lisp正在标准化当时已知的内容以及用户认为开发所需的语言(对象系统CLOS是一种新颖的,基于早期使用类似对象系统的经验)的软件.Common Lisp并不是为了拥有理论上最令人愉悦的Lisp方言而设计的 - 它被设计成具有强大的Lisp,可以高效地实现软件.
我们可以解决这个问题并说,传递宏是不可能的.开发人员必须提供一个相同名称的函数,我们传递这个函数.
但随后(funcall #'foo 1 2)
并(foo 1 2)
会调用不同的机器?在第一种情况下,函数foo
和第二种情况下我们使用宏foo
为我们生成代码?真?我们(作为人类程序员)是否想要这个?我认为不是 - 看起来它使编程变得更加复杂.
从实用的角度来看:宏及其背后的机制已经足够复杂,大多数程序员都难以在实际代码中处理它.它们使得调试和代码理解对于人类来说更加困难.从表面上看,宏使代码更容易阅读,但价格是需要理解代码扩展过程和结果.找到将宏进一步集成到语言设计中的方法并非易事.
readscheme.org对宏相关研究提出了一些建议.方案:宏
Common Lisp怎么样?
Common Lisp的功能,提供了可以是一流的(存储,传来传去,...)和词法范围命名为他们(DEFUN
,FLET
,LABELS
,FUNCTION
,LAMBDA
).
Common Lisp提供全局宏(DEFMACRO
)和本地宏(MACROLET
).
Common Lisp提供全局编译器宏(DEFINE-COMPILER-MACRO).
使用编译器宏,可以为符号和编译器宏提供函数或宏.Lisp系统可以决定在宏或函数上优先使用编译器宏.它也可以完全忽略它们.此机制主要用于用户编程特定的优化.因此,它没有解决任何与宏相关的问题,但提供了一种编程全局优化的实用方法.
Gar*_*ees 10
我认为Common Lisp的两个名称空间(函数和值),而不是三个(宏,函数和值),是一个历史偶然事件.
Early Lisps(在20世纪60年代)以不同的方式表示函数和值:值作为运行时堆栈上的绑定,以及作为附加到符号表中的符号的属性的函数.当Common Lisp在20世纪80年代被标准化时,这种实现的差异导致了两个命名空间的规范.有关此决定的解释,请参阅Richard Gabriel的论文" 功能细胞和价值细胞中分离的技术问题".
宏(及其祖先,FEXPR,不评估其参数的函数)以与函数相同的方式存储在符号表中的许多Lisp实现中.如果已经指定了第三个命名空间(用于宏),那么这些实现会很不方便,并且会导致许多程序出现向后兼容性问题.
有关FEXPR,宏和其他特殊形式的历史,请参阅Kent Pitman的Lisp中的特殊形式论文.
(注意:Kent Pitman的网站不适合我,所以我通过archive.org链接到论文.)
归档时间: |
|
查看次数: |
1052 次 |
最近记录: |