玩连续剧。目标是创建将接收另一个函数作为参数的函数,以及执行量 - 并返回函数,该函数将应用参数给定量的次数。
实施看起来非常明显
def n_times[T](func:T=>T,count:Int):T=>T = {
@tailrec
def n_times_cont(cnt:Int, continuation:T=>T):T=>T= cnt match {
case _ if cnt < 1 => throw new IllegalArgumentException(s"count was wrong $count")
case 1 => continuation
case _ => n_times_cont(cnt-1,i=>continuation(func(i)))
}
n_times_cont(count, func)
}
def inc (x:Int) = x+1
val res1 = n_times(inc,1000)(1) // Works OK, returns 1001
val res = n_times(inc,10000000)(1) // FAILS
但没有问题 - 此代码因StackOverflow错误而失败。为什么这里没有尾调用优化?
我在Eclipse中使用Scala插件运行它,然后返回 线程“main”java.lang.StackOverflowError中的异常 在scala.runtime.BoxesRunTime.boxToInteger(未知来源) 在Task_Mult $$ anonfun $ 1.apply(Task_Mult.scala:25) 在Task_Mult $$ anonfun $ n_times_cont $ 1 $ 1.apply(Task_Mult.scala:18)
P.S。
F#代码,几乎是直接翻译,工作没有任何问题
let n_times_cnt func count =
let rec n_times_impl count' continuation =
match count' with
| _ when count'<1 -> failwith "wrong count"
| 1 -> continuation
| _ -> n_times_impl (count'-1) (func >> continuation)
n_times_impl count func
let inc x = x+1
let res = (n_times_cnt inc 10000000) 1
printfn "%o" res
答案 0 :(得分:4)
Scala标准库在scala.util.control.TailCalls
中具有蹦床的实现。所以重新审视你的实现......当你使用continuation(func(t))
构建嵌套调用时,那些是尾调用,只是没有被编译器优化。所以,让我们构建一个T => TailRec[T]
,其中堆栈帧将被堆中的对象替换。然后返回一个函数,该函数将获取参数并将其传递给该trampolined函数:
import util.control.TailCalls._
def n_times_trampolined[T](func: T => T, count: Int): T => T = {
@annotation.tailrec
def n_times_cont(cnt: Int, continuation: T => TailRec[T]): T => TailRec[T] = cnt match {
case _ if cnt < 1 => throw new IllegalArgumentException(s"count was wrong $count")
case 1 => continuation
case _ => n_times_cont(cnt - 1, t => tailcall(continuation(func(t))))
}
val lifted : T => TailRec[T] = t => done(func(t))
t => n_times_cont(count, lifted)(t).result
}
答案 1 :(得分:1)
我可能在这里错了,但我怀疑n_times_cont
内部函数被正确转换为使用尾递归;罪魁祸首不在那里。
一旦你应用了main函数的结果,堆栈会被收集的continuation
闭包(即i=>continuation(func(i))
)炸毁,这些闭包对你的inc
方法进行10000000次嵌套调用。
实际上你可以试试
scala> val rs = n_times(inc, 1000000)
rs: Int => Int = <function1> //<- we're happy here
scala> rs(1) //<- this blows up the stack!
顺便说一下,你可以重写
i=>continuation(func(i))
as
continuation compose func
为了更好的可读性