clojure.core apply 函数的宏形式

问题描述 投票:0回答:1

clojure.core
中有
apply
函数,以多参数风格编写(大概是为了提高效率)。

但是我问是否有这样的实现:

(defmacro apply [f coll] (conj (seq coll) f))

不会更好。

我知道宏的效率低于函数, 但这真的有那么大的区别吗?

或者,是否可以将此宏写为函数 (我尝试过但失败了,但这并不能证明我没有编写宏的经验)?

performance clojure macros apply
1个回答
0
投票

apply
是函数而不是宏的原因是它必须如此。您定义的宏版本没有用:它不起作用。

但为什么不呢?这似乎是一个合理的想法。让我们尝试一下,但称之为

mapply
,这样我们仍然可以参考现有的
apply
:

=> (defmacro mapply [f coll] (conj (seq coll) f))

我打赌你做到了这一点,我打赌你甚至测试过它:你可以在绝对最简单的情况下使用

mapply
,就像这样:

=> (macroexpand '(mapply + [1 2 3]))
(+ 1 2 3) 
=> (mapply + [1 2 3])
6

问题是这没有用:你写了

(mapply + [1 2 3])
,而写
(+ 1 2 3)
也同样容易(真的更容易)。
apply
的真正价值在于你可以将它应用于运行时确定的列表:当参数列表在编译时固定时,你不需要
apply
,因为你可以只写
(f x y)
而不是
(apply f [x y])
.

所以让我们在运行时计算的列表上尝试您的版本:

=> (apply + (range 4))
6
=> (macroexpand '(mapply + (range 4))
(+ range 4)
=> (mapply + (range 4))
Execution error (ClassCastException) at user/eval2051 (REPL:1).
clojure.core$range cannot be cast to java.lang.Number

发生什么事了?你的

mapply
看到
(range 4)
是一个集合,所以它把
+
放在它的前面。您想要是评估
(range 4)
,生成一个列表,然后以某种方式添加其元素。但因为
mapply
在编译时工作,所以它看到的列表是
(range 4)
:一个包含元素
range
和 4` 的二元素列表。

这不仅仅是

mapply
定义中的一些简单错误:不可能将
mapply
定义为宏(除非作为一个在编译时不起作用并在运行时委托给某些函数的微不足道的宏)。对于一个更明显不可能的示例,请考虑从函数内部调用您的
mapply

=> (defn sum [xs] (apply + xs))
=> (sum (range 4))
6
=> (defn msum [xs] (mapply + xs))
Syntax error macroexpanding mapply at (REPL:1:17).
Don't know how to create ISeq from: clojure.lang.Symbol

由于

mapply
是一个宏,它当然在编译时运行,即在我们定义msum
时。它只有一次扩展的机会,而不知道我们将来可能会通过什么
xs
。因此,当它查看 
xs
 时,它只会看到符号 
xs
,而不是某个列表。因此,
seq
调用不可避免地会失败。再说一次,没有其他实现可以工作,因为你根本不知道在编译时列表有多少元素,并且无法扩展到正确的调用。

作为最后的对待,请考虑另一个原因

apply

 不能是宏:您可以将函数应用于无限的参数列表,但如果您尝试生成无限长的宏扩展,编译器将度过一段非常悲伤的时光.

=> (defn apply-to-nats [f] (apply f (range))) => (apply-to-nats (fn [& args] (first args))) 0 => (defmacro mapply-to-nats [f] `(f ~@(range))) => (mapply-to-nats (fn [& args] (first args))) Syntax error (OutOfMemoryError) compiling at (REPL:1:1). GC overhead limit exceeded
发生什么事了?编译器循环运行,直到我的系统内存不足,尝试编译

((fn [& args] (first args)) 0 1 2 3 4 ...)

© www.soinside.com 2019 - 2024. All rights reserved.