我知道编写尾递归函数至少有两种样式。以sum
函数为例:
def sum1(xs: List[Int]): Int = {
def loop(xs: List[Int], acc: Int): Int = xs match {
case Nil => acc
case x :: xs1 => loop(xs1, acc + x)
}
loop(xs, 0)
}
VS
def sum2(xs: List[Int], acc: Int = 0): Int = xs match {
case Nil => acc
case x :: xs1 => sum2(xs1, x + acc)
}
我注意到第一种风格(内部循环功能)比第二种更常见。是否有任何理由喜欢它或者仅仅是风格的区别?
答案 0 :(得分:1)
有几个理由喜欢第一种符号。
首先,您要清楚地向读者说明外部实施的内部实施情况。
其次,在您的示例中,种子值是一个非常简单的,您可以直接作为默认参数,但您的种子值可能是一个非常复杂的计算对象,需要比默认值更长的init。如果这个init例如需要异步完成,那么你肯定希望将它从默认值中删除并使用Futures或w / e进行管理。
最后,正如Didier所提到的,sum1的类型是List [Int]中的函数 - > Int(有意义),而sum2的类型是来自(List [Int],Int)的函数 - > Int不太有意义。而且,这意味着传递sum1比sum2更容易。例如,如果你有一个封装了Int列表的对象,并且你想在它上面提供合成器函数,你可以这样做(伪代码,我现在没有正确编写它的repl ):
class MyFancyList[T](val seed: List[T]) = {
type SyntFunction = (List[T] => Any)
var functions = Set[SyntFunction]
def addFunction(f: SyntFunction) = functions += f
def computeAll = {
for {
f <- functions
}
yield {
f(seed)
}
}
}
你可以这样做:
def concatStrings(list:List[Int]) = {
val listOfStrings = for {
n <- list
}
yield {
n+""
}
listOfStrings.mkString
}
val x = MyFancyList(List(1, 2, 3))
x.addFunction(sum1)
x.addFunction(concatStrings)
x.computeAll == List(6, "123")
但你不能添加sum2(至少不那么容易)