我已经在Scala中实现了Sphere函数(获取元素列表,对每个元素求平方并返回总和)。我想将此函数转换为尾递归。我的代码在这里
def Sphere(list: List[Double]): Double = {
val power = list.map(math.pow(_, 2))
power.reduce(_+_)
}
答案 0 :(得分:5)
它看起来像:
@tailrec
def Sphere(list: List[Double], result: Double = 0): Double = list match {
case Nil => result
case x::xs => Sphere(xs, result + math.pow(x, 2))
}
println(Sphere(List(1,3,4,5)))
使用@tailrec
,请确保它实际上是尾部递归的(编译将失败)。
重要的是:
x
是进行计算的列表的头xs
是列表的其余部分( tail )-在其中附加递归函数-直到列表为空> Nil
。答案 1 :(得分:4)
如果要保留相同的函数签名,则需要使用嵌套的递归函数:
def Sphere(list: List[Double]): Double = {
@annotation.tailrec
def loop(rem: List[Double], res: Double): Double =
rem match {
case hd :: tail => loop(tail, res + hd*hd)
case _ => res
}
loop(list, 0)
}
您可以将其编写为单个尾部递归函数,但这需要更改函数签名以添加虚假的额外参数。这可以说是不好的设计,因为实现是通过接口泄漏出去的。如果这是一种方法,这还需要将该函数标记为final
。
答案 2 :(得分:0)
添加到上面的答案中,如果您使用foldLeft,则它已经优化了尾部调用
def Sphere(list: List[Double]): Double = {
list.foldLeft(0.0)((res, elem) => res + elem * elem)
}
最好使用库版本,并且在东西可用时不要为编译器添加额外的检查(@tailrec)。
** foldLeft与reduceLeft相同,除了它以额外的初始值作为参数并且在集合为空时不引发异常。
** foldLeft的实际实现出于性能原因最终使用了while循环,但这再次是尾递归的好处,使其与循环一样快。