尾递归:内部"循环"累加器的功能或默认值

时间:2014-11-24 18:07:29

标签: scala recursion functional-programming

我知道编写尾递归函数至少有两种样式。以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)
}

我注意到第一种风格(内部循环功能)比第二种更常见。是否有任何理由喜欢它或者仅仅是风格的区别?

1 个答案:

答案 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(至少不那么容易)