将球面函数转换为尾递归函数

时间:2019-06-14 11:03:41

标签: scala tail-recursion

我已经在Scala中实现了Sphere函数(获取元素列表,对每个元素求平方并返回总和)。我想将此函数转换为尾递归。我的代码在这里

def Sphere(list: List[Double]): Double = {
  val power = list.map(math.pow(_, 2))
  power.reduce(_+_)
}

3 个答案:

答案 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循环,但这再次是尾递归的好处,使其与循环一样快。