Scala:为什么递归在 Scala 中被认为比使用循环更好? (仅仅因为可变性?)(尾递归与累加器)
Scala: Why is Recursion considered better in Scala than using Loops? (Just because of Mutability?) (Tail Recursion Vs Accumulators)
我知道在使用循环时,可变性会出现并使事情难以追踪。但是在 Scala 中递归被认为是循环的仅仅是因为可变性吗?
此外,我了解到尾递归不会添加到您的调用堆栈中,但并非所有问题都可以使用尾递归解决,对吗?使用似乎也足以避免堆栈溢出情况的基于累加器的方法怎么样?尾递归和基于累加器的递归方法之间哪个性能更高?
简而言之,我有两个问题:
- 由于不能使用尾递归来解决所有循环问题(或者可以吗?),对于某些特定用例,循环不是更好的解决方案还是唯一的解决方案?
- 尾递归和基于累加器的递归有什么区别?根据 space 复杂度和占用的调用堆栈,哪个性能更高?
编辑 1:
在基于累加器的递归(阶乘函数)中转换的非尾递归函数示例(使用中间尾递归函数):
无蓄能器:
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
带累加器:
def tailRecFactorial(n: Int): BigInt = {
@tailrec
def factorialHelper(x: Int, accumulator: BigInt): BigInt = {
if (x <= 1) accumulator
else factorialHelper(x - 1, x * accumulator)
}
factorialHelper(n, 1)
}
嗯,首先,不确定基于累加器的递归是什么意思?
其次,Scala只有while
个循环,所有while
个循环都可以用(tail)递归;据我所知。
第三,是的,我们更喜欢递归而不是 while
以避免可变性。
但是,递归本身还是太低级了。通常,您应该更喜欢高阶组合器,例如 map
或 foldLeft
第四,虽然可变性包含在单个方法中是可以的,但通常最好在所有地方保持一致并保持一切不可变(除非你有很好的理由否则)而不是随机混合可变性和不变性只是因为。
Scala 是一种函数式语言,函数式代码通常被认为比非函数式代码更好。对 while
的反对意见是它依赖于在迭代过程中更改的值,因此不能发挥作用。
回答具体问题:
任何循环都可以表示为纯尾递归函数,但要确定将什么状态传递给递归调用可能会变得非常棘手。
“尾递归”和“基于累加器的递归”并不互斥。尾递归函数是任何调用自身作为至少一个代码路径上的最后一个动作的函数。基于累加器的递归只是意味着将部分结果传递给递归调用,作为将非尾递归函数转换为尾递归函数的一种方式。
我知道在使用循环时,可变性会出现并使事情难以追踪。但是在 Scala 中递归被认为是循环的仅仅是因为可变性吗?
此外,我了解到尾递归不会添加到您的调用堆栈中,但并非所有问题都可以使用尾递归解决,对吗?使用似乎也足以避免堆栈溢出情况的基于累加器的方法怎么样?尾递归和基于累加器的递归方法之间哪个性能更高?
简而言之,我有两个问题:
- 由于不能使用尾递归来解决所有循环问题(或者可以吗?),对于某些特定用例,循环不是更好的解决方案还是唯一的解决方案?
- 尾递归和基于累加器的递归有什么区别?根据 space 复杂度和占用的调用堆栈,哪个性能更高?
编辑 1:
在基于累加器的递归(阶乘函数)中转换的非尾递归函数示例(使用中间尾递归函数):
无蓄能器:
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
带累加器:
def tailRecFactorial(n: Int): BigInt = {
@tailrec
def factorialHelper(x: Int, accumulator: BigInt): BigInt = {
if (x <= 1) accumulator
else factorialHelper(x - 1, x * accumulator)
}
factorialHelper(n, 1)
}
嗯,首先,不确定基于累加器的递归是什么意思?
其次,Scala只有while
个循环,所有while
个循环都可以用(tail)递归;据我所知。
第三,是的,我们更喜欢递归而不是 while
以避免可变性。
但是,递归本身还是太低级了。通常,您应该更喜欢高阶组合器,例如 map
或 foldLeft
第四,虽然可变性包含在单个方法中是可以的,但通常最好在所有地方保持一致并保持一切不可变(除非你有很好的理由否则)而不是随机混合可变性和不变性只是因为。
Scala 是一种函数式语言,函数式代码通常被认为比非函数式代码更好。对 while
的反对意见是它依赖于在迭代过程中更改的值,因此不能发挥作用。
回答具体问题:
任何循环都可以表示为纯尾递归函数,但要确定将什么状态传递给递归调用可能会变得非常棘手。
“尾递归”和“基于累加器的递归”并不互斥。尾递归函数是任何调用自身作为至少一个代码路径上的最后一个动作的函数。基于累加器的递归只是意味着将部分结果传递给递归调用,作为将非尾递归函数转换为尾递归函数的一种方式。