我正在查看以下代码
http://aperiodic.net/phil/scala/s-99/p26.scala
具体地
def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] =
ls match {
case Nil => Nil
case sublist@(_ :: tail) => f(sublist) ::: flatMapSublists(tail)(f)
}
我得到大值的StackOverflowError可能是因为函数不是尾递归的。有没有办法转换函数以适应大数?
答案 0 :(得分:6)
绝对不是尾递归。 f(sublist) :::
正在修改递归调用的结果,使其成为一个普通的堆栈递归而不是尾递归。
确保函数是尾递归的一种方法是将@annotation.tailrec
放在任何您期望尾递归的函数上。如果无法执行尾调用优化,编译器将报告错误。
为此,我会添加一个实际上是尾递归的小辅助函数:
def flatMapSublistsTR[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] = {
@annotation.tailrec
def helper(r: List[B], ls: List[A]): List[B] = {
ls match {
case Nil => r
case sublist@(_ :: tail) => helper(r ::: f(sublist), tail)
}
}
helper(Nil, ls)
}
由于我不明显的原因,结果的顺序与原始函数不同。但是,它看起来像是有效的: - )固定。
答案 1 :(得分:3)
以下是实现该功能的另一种方法:
scala> def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] =
| List.iterate(ls, ls.size)(_.tail).flatMap(f)
flatMapSublists: [A, B](ls: List[A])(f: List[A] => List[B])List[B]
简单比较dave的flatMapSublistsTR和我的:
scala> def time(count: Int)(call : => Unit):Long = {
| val start = System.currentTimeMillis
| var cnt = count
| while(cnt > 0) {
| cnt -= 1
| call
| }
| System.currentTimeMillis - start
| }
time: (count: Int)(call: => Unit)Long
scala> val xs = List.range(0,100)
scala> val fn = identity[List[Int]] _
fn: List[Int] => List[Int] = <function1>
scala> time(10000){ flatMapSublists(xs)(fn) }
res1: Long = 5732
scala> time(10000){ flatMapSublistsTR(xs)(fn) }
res2: Long = 347232
将flatMapSublistsTR方法实现为:
def flatMapSublistsTR[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] = {
@annotation.tailrec
def helper(r: List[B], ls: List[A]): List[B] = {
ls match {
case Nil => r
case sublist@(_ :: tail) => helper(r ::: f(sublist), tail)
}
}
helper(Nil, ls)
}
答案 2 :(得分:1)
def flatMapSublists2[A,B](ls: List[A], result: List[B] = Nil)(f: (List[A]) => List[B]): List[B] =
ls match {
case Nil => result
case sublist@(_ :: tail) => flatMapSublists2(tail, result ++ f(sublist))(f)
}
您通常只需要添加一个结果结果参数,从一次迭代到下一次迭代,并在结尾处吐出结果,而不是将结尾添加到列表中。
此外,sublist@
事情可以简化为
case _ :: tail => flatMapSublists2(tail, result ++ f(ls))(f)
偏离主题:这是我如何解决问题26,而不需要像上面那样的辅助方法。如果你可以使这个尾递归,有一个金星。
def combinations[A](n: Int, lst: List[A]): List[List[A]] = n match {
case 1 => lst.map(List(_))
case _ => lst.flatMap(i => combinations (n - 1, lst.dropWhile(_ != i).tail) map (i :: _))
}