以下是其中一个答案中提出的声明:https://softwareengineering.stackexchange.com/a/136146/18748
尝试一两种功能语言。尝试实施 Erlang中的阶乘使用递归并观察你的下巴击中地板 什么时候20000!在5秒内返回(站点中没有堆栈溢出)
为什么它比在Java / C / C ++ / Python(任何)中使用递归/迭代更快/更有效?实现这一目标的基本数学/理论概念是什么?不幸的是,我从未接触过本科生的函数式编程(以'C'开头),所以我可能只是不知道'为什么'。
答案 0 :(得分:2)
在Java或C ++中,递归解决方案可能会更慢,更笨,但我们不会在Java和C ++中这样做,所以。 :)
至于为什么,函数式语言总是非常大量地使用递归(因为默认情况下它们要么必须跳过箍,要么根本不允许修改变量,这些变量本身使得大多数迭代形式无效或者完全不可能)。因此,为了具有竞争力,他们必须有效地优化地狱。
几乎所有这些都实现了一个称为“尾调用消除”的优化,它使用当前调用的堆栈空间进行下一次调用,并将“调用”变为“跳转”。这个小小的改变基本上会将递归转换为迭代,但不要提醒FPers。当涉及到过程语言中的迭代和函数式中的递归时,两者将证明性能相等。 (如果其中任何一个仍然更快,那么它将是迭代。)
其余的是图书馆和数字类型等。体面的数学代码==>不错的数学表现。没有什么可以保持程序或OO语言的优化类似,除了大多数人不关心那么多。另一方面,功能性程序员喜欢对他们如何轻松地计算Fibonacci序列和20000的内容有所了解!以及我们大多数人永远不会使用的其他数字。
答案 1 :(得分:1)
基本上,函数式语言使递归变得便宜。
答案 2 :(得分:1)
Lazy evaluation:“当使用延迟评估时,表达式一旦被绑定到变量就不会被评估,但是当评估者被迫生成表达式的值时。”
这两个概念都在这个question about various ways of implementing factorial in Clojure中说明。您还可以在Stuart Halloway和Aaron Bedra的书Programming Clojure中找到关于懒惰序列和TCO的详细讨论。
从Clojure编程中采用的以下函数创建了一个具有Fibonacci序列的前1M个成员的惰性序列,并实现了第十万个成员:
user=> (time (nth (take 1000000 (map first (iterate (fn [[a b]] [b (+ a b)]) [0N 1N]))) 100000))
"Elapsed time: 252.045 msecs"
25974069347221724166155034....
(20900 total digits)
(512MB堆,Intel Core i7,2.5GHz)
答案 3 :(得分:1)
它不更快(比什么?),它与尾调用优化无关(在这里抛出一个流行语是不够的,还应该解释为什么尾调用优化应该是比循环更快?根本不是这样的!)
请注意,我不是一个功能性的编程仇恨者,相反!但传播神话不适用于函数式编程。
BTW,这里有任何人实际尝试了多长时间来计算(和打印,这应该消耗至少50%的所需CPU周期)20000!我做了:
main _ = println (product [2n..20000n])
这是一种编译为Java的JVM语言,它使用Java大整数(已知很慢)。它也会受到JVM启动成本的影响。并且它不是最快的方法(显式递归可以保存列表构造,例如)。
结果是:
181920632023034513482764175686645876607160990147875264
...
... many many lines with digits
...
000000000000000000000000000000000000000000000000000000000000000
real 0m3.330s
user 0m4.472s
sys 0m0.212s
(在英特尔®酷睿™i3 CPU M 350 @ 2.27GHz×4上)
我们可以安全地假设C与GMP相同甚至不会使用50%的时间。
因此,功能更快是一个神话,而功能更慢。它甚至不是一个神话,只要一个人没有说它比它更快/更慢,这简直是胡说八道。