我理解 reduce 和 apply 之间的概念差异:
reduce
apply
(reduce + (list 1 2 3 4 5)) ; translates to: (+ (+ (+ (+ 1 2) 3) 4) 5) (apply + (list 1 2 3 4 5)) ; translates to: (+ 1 2 3 4 5)
然而,哪一个更惯用的clojure?这种方式或其他方式有什么不同吗?从我的(有限的)性能测试来看,似乎 reduce 有点快 .
reduce 和 apply 当然只对于需要在变量arity情况下查看所有参数的关联函数而言是等效的(就返回的最终结果而言) . 当它们在结果方面是等价的时候,我会说 apply 总是完全惯用的,而 reduce 在很多常见情况下都是等效的 - 并且可能会在很短的时间内完成 . 以下是我相信这一点的理由 .
+ 本身就 reduce 实现了变量arity案例(超过2个参数) . 事实上,对于任何变量,关联函数来说,这似乎是一种非常明智的方法: reduce 有可能执行一些优化以加快速度 - 可能通过最近在主人身上禁用的1.2新奇事物 internal-reduce ,但是希望将来能够重新引入 - 在vararg案例中,在每个可能从中受益的函数中复制将是愚蠢的 . 在这种常见情况下, apply 只会增加一点开销 . (注意没什么好担心的 . )
+
internal-reduce
另一方面,复杂的函数可能会利用一些优化机会,这些机会不够通用,无法构建到_628911中;然后 apply 会让你利用那些,而 reduce 可能会让你失望 . 在实践中出现的后一种情况的一个很好的例子是由 str 提供的:它在内部使用了 StringBuilder ,并且将从使用 apply 而不是 reduce 中获益 .
str
StringBuilder
所以,如果有疑问,我会说 apply ;如果你碰巧知道它不会给你带来任何超过 reduce 的东西(并且这不太可能很快改变),如果你愿意,可以随意使用 reduce 来减少这种微小的不必要的开销 .
对于看这个答案的新手,小心,它们不一样:
(apply hash-map [:a 5 :b 6]) ;= {:a 5, :b 6} (reduce hash-map [:a 5 :b 6]) ;= {{{:a 5} :b} 6}
意见各不相同 - 在更大的Lisp世界中, reduce 绝对被认为是更惯用的 . 首先,已经讨论过可变问题 . 此外,当 apply 应用于非常长的列表时,由于它们如何处理参数列表,一些Common Lisp编译器实际上会失败 .
然而,在我的圈子中的Clojurists中,在这种情况下使用 apply 似乎更常见 . 我发现它更容易理解并且也更喜欢它 .
它在这种情况下没有区别,因为它是一个特殊情况,可以应用于任意数量的参数 . Reduce是一种将需要固定数量的参数(2)的函数应用于任意长的参数列表的方法 .
我通常发现自己更倾向于使用reduce来处理任何类型的集合 - 它表现良好,并且通常是非常有用的功能 .
我将使用apply的主要原因是,如果参数在不同位置意味着不同的东西,或者如果你有几个初始参数但是想要从集合中获取其余参数,例如
(apply + 1 2 other-number-list)
在这种特殊情况下,我更喜欢 reduce ,因为它更多 readable :当我读到时
(reduce + some-numbers)
我立即知道你正在将一个序列变成一个值 .
使用 apply 我必须考虑应用了哪个函数:“啊,它是 + 函数,所以我得到......一个数字” . 略显简单明了 .
当使用类似的简单函数时,使用哪一个并不重要 .
一般来说,想法是减少是一种累积操作 . 您将当前累积值和一个新值呈现给累积函数,结果是下一次迭代的累积值 . 所以,你的迭代看起来像:
cum-val[i+1] = F( cum-val[i], input-val[i] ) ; please forgive the java-like syntax!
对于apply,我们的想法是你试图调用一个期望一些标量参数的函数,但它们目前在一个集合中,需要被拔出 . 所以,而不是说:
vals = [ val1 val2 val3 ] (some-fn (vals 0) (vals 1) (vals2))
我们可以说:
(apply some-fn vals)
它被转换为等同于:
(some-fn val1 val2 val3)
因此,使用“apply”就像是在序列周围“删除括号” .
关于这个话题稍晚但我在阅读这个例子后做了一个简单的实验 . 这是我的repl的结果,我不能从响应中推断出任何东西,但似乎在reduce和apply之间存在某种缓存 .
user=> (time (reduce + (range 1e3))) "Elapsed time: 5.543 msecs" 499500 user=> (time (apply + (range 1e3))) "Elapsed time: 5.263 msecs" 499500 user=> (time (apply + (range 1e4))) "Elapsed time: 19.721 msecs" 49995000 user=> (time (reduce + (range 1e4))) "Elapsed time: 1.409 msecs" 49995000 user=> (time (reduce + (range 1e5))) "Elapsed time: 17.524 msecs" 4999950000 user=> (time (apply + (range 1e5))) "Elapsed time: 11.548 msecs" 4999950000
查看clojure的源代码,使用internal-reduce减少其非常干净的递归,虽然没有找到任何关于apply的实现 . Clojure实现for apply内部调用reduce,它由repl缓存,似乎解释了第4次调用 . 有人可以澄清这里真的发生了什么吗?
申请的优点是给定功能(在这种情况下)可以应用由预先存在的干预参数与结束集合形成的参数列表 . Reduce是一个抽象来处理为每个函数应用函数的集合项,并且不适用于变量args情况 .
(apply + 1 2 3 [3 4]) => 13 (reduce + 1 2 3 [3 4]) ArityException Wrong number of args (5) passed to: core/reduce clojure.lang.AFn.throwArity (AFn.java:429)
9 回答
reduce
和apply
当然只对于需要在变量arity情况下查看所有参数的关联函数而言是等效的(就返回的最终结果而言) . 当它们在结果方面是等价的时候,我会说apply
总是完全惯用的,而reduce
在很多常见情况下都是等效的 - 并且可能会在很短的时间内完成 . 以下是我相信这一点的理由 .+
本身就reduce
实现了变量arity案例(超过2个参数) . 事实上,对于任何变量,关联函数来说,这似乎是一种非常明智的方法:reduce
有可能执行一些优化以加快速度 - 可能通过最近在主人身上禁用的1.2新奇事物internal-reduce
,但是希望将来能够重新引入 - 在vararg案例中,在每个可能从中受益的函数中复制将是愚蠢的 . 在这种常见情况下,apply
只会增加一点开销 . (注意没什么好担心的 . )另一方面,复杂的函数可能会利用一些优化机会,这些机会不够通用,无法构建到_628911中;然后
apply
会让你利用那些,而reduce
可能会让你失望 . 在实践中出现的后一种情况的一个很好的例子是由str
提供的:它在内部使用了StringBuilder
,并且将从使用apply
而不是reduce
中获益 .所以,如果有疑问,我会说
apply
;如果你碰巧知道它不会给你带来任何超过reduce
的东西(并且这不太可能很快改变),如果你愿意,可以随意使用reduce
来减少这种微小的不必要的开销 .对于看这个答案的新手,
小心,它们不一样:
意见各不相同 - 在更大的Lisp世界中,
reduce
绝对被认为是更惯用的 . 首先,已经讨论过可变问题 . 此外,当apply
应用于非常长的列表时,由于它们如何处理参数列表,一些Common Lisp编译器实际上会失败 .然而,在我的圈子中的Clojurists中,在这种情况下使用
apply
似乎更常见 . 我发现它更容易理解并且也更喜欢它 .它在这种情况下没有区别,因为它是一个特殊情况,可以应用于任意数量的参数 . Reduce是一种将需要固定数量的参数(2)的函数应用于任意长的参数列表的方法 .
我通常发现自己更倾向于使用reduce来处理任何类型的集合 - 它表现良好,并且通常是非常有用的功能 .
我将使用apply的主要原因是,如果参数在不同位置意味着不同的东西,或者如果你有几个初始参数但是想要从集合中获取其余参数,例如
在这种特殊情况下,我更喜欢
reduce
,因为它更多 readable :当我读到时我立即知道你正在将一个序列变成一个值 .
使用
apply
我必须考虑应用了哪个函数:“啊,它是+
函数,所以我得到......一个数字” . 略显简单明了 .当使用类似的简单函数时,使用哪一个并不重要 .
一般来说,想法是减少是一种累积操作 . 您将当前累积值和一个新值呈现给累积函数,结果是下一次迭代的累积值 . 所以,你的迭代看起来像:
对于apply,我们的想法是你试图调用一个期望一些标量参数的函数,但它们目前在一个集合中,需要被拔出 . 所以,而不是说:
我们可以说:
它被转换为等同于:
因此,使用“apply”就像是在序列周围“删除括号” .
关于这个话题稍晚但我在阅读这个例子后做了一个简单的实验 . 这是我的repl的结果,我不能从响应中推断出任何东西,但似乎在reduce和apply之间存在某种缓存 .
查看clojure的源代码,使用internal-reduce减少其非常干净的递归,虽然没有找到任何关于apply的实现 . Clojure实现for apply内部调用reduce,它由repl缓存,似乎解释了第4次调用 . 有人可以澄清这里真的发生了什么吗?
申请的优点是给定功能(在这种情况下)可以应用由预先存在的干预参数与结束集合形成的参数列表 . Reduce是一个抽象来处理为每个函数应用函数的集合项,并且不适用于变量args情况 .