为什么 Scala 不进行尾调用优化? [英] why scala doesn't make tail call optimization?

查看:46
本文介绍了为什么 Scala 不进行尾调用优化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

只是玩延续.目标是创建一个函数,该函数将接收另一个函数作为参数和执行量 - 并返回将应用参数给定次数的函数.

Just playing with continuations. The goal is to create function which will receive another function as parameter, and execution amount - and return function which will apply parameter given amount times.

实现看起来很明显

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 错误而失败.为什么这里没有尾调用优化?

But there is no problem - this code fails with StackOverflow error. Why there is no tail-call optimization here?

我在 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)

I'm running it in Eclipse using Scala plugin, and it returns Exception in thread "main" java.lang.StackOverflowError at scala.runtime.BoxesRunTime.boxToInteger(Unknown Source) at Task_Mult$$anonfun$1.apply(Task_Mult.scala:25) at Task_Mult$$anonfun$n_times_cont$1$1.apply(Task_Mult.scala:18)

附言

几乎是直接翻译的 F# 代码运行没有任何问题

F# code, which is almost direct translation, is working without any issues

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

推荐答案

Scala 标准库scala.util.control.TailCalls 中实现了蹦床.所以重新审视你的实现......当你用 continuation(func(t)) 构建嵌套调用时,那些是尾调用,只是没有被编译器优化.所以,让我们建立一个 T =>TailRec[T],其中堆栈帧将被堆中的对象替换.然后返回一个函数,该函数将接受参数并将其传递给该蹦床函数:

The Scala standard library has an implementation of trampolines in scala.util.control.TailCalls. So revisiting your implementation... When you build up the nested calls with continuation(func(t)), those are tail calls, just not optimized by the compiler. So, let's build up a T => TailRec[T], where the stack frames will be replaced with objects in the heap. Then return a function that will take the argument and pass it to that trampolined function:

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
}

这篇关于为什么 Scala 不进行尾调用优化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆