我有一个Scala函数,想知道是否可能将其转换为尾递归函数。
def get_f(f: Int => Int, x: Int, y: Int): Int = x match {
case 0 => y
case _ => f(get_f(f, x - 1, y))
}
我有一个Scala函数,想知道是否可能将其转换为尾递归函数。
def get_f(f: Int => Int, x: Int, y: Int): Int = x match {
case 0 => y
case _ => f(get_f(f, x - 1, y))
}
f
函数递归地应用于结果,共x
次。这与将其应用于y
,x
次相同。此外,我建议您使用if else
而不是模式匹配。@tailrec
def get_f(f: Int => Int, x: Int, y: Int): Int =
if(x == 0) y
else get_f(f, x - 1, f(y))
加上@tailrec
注解以确保它是尾递归的
我可以告诉你使用foldLeft
在Range
上也可以达到相同的结果,像这样:
def get_f(f: Int => Int, x: Int, y: Int) =
(0 until x).foldLeft(y)((acc, _) => f(acc))
foldRight
在内部创建了一个n元素的列表(因为它在reversed
迭代器上调用了foldLeft
),所以最好使用foldLeft
。 - adamwy让我们从减少非尾递归版本的参数开始,以便清楚地了解它实际上是做什么的:
def get_f(f: Int => Int, x: Int, y: Int) = {
def get_f_impl(x: Int): Int = x match {
case 0 => y
case _ => f(get_f_impl(x - 1))
}
get_f_impl(x)
}
这个想法是,你将f
函数应用x次于初始值y
。因此,很明显你可以这样做以使其成为尾递归:
def get_f(f: Int => Int, x: Int, y: Int) = {
@tailrec def get_f_impl(acc: Int, x: Int): Int =
if (x == 0) acc else get_f_impl(f(acc), x - 1)
get_f_impl(y, x)
}
REPL调查:
您的原始实现:
scala> get_f(_ + 1, 4, 0)
res6: Int = 4
scala> get_f(_ + 1, 4, 0)
res0: Int = 4
Tailrec 实现:
scala> get_f(_ + 1, 4, 0)
res3: Int = 4
附注:对于更复杂的情况,跳板可能更适合:https://espinhogr.github.io/scala/2015/07/12/trampolines-in-scala.html
附注2:您也可以尝试:
Scala - 将函数组合n次 @adamw注意到它将分配大小为n的列表,因此可能不是非常高效
在scalaz中的Endo.mulitply
(您的f:Int => Int
实际上是一个自同态):https://dev59.com/eWsz5IYBdhLWcg3w-89v#7530783 - 不确定效率如何
get_f_impl
调用了get_f
。 - adamwyf
应用n次。 - dk14f(x-1)
的结果来调用get_f_impl
而不是使用x-1
。 - adamwyf
进行转换后,您将此计数器转换为一个值。 - adamwy根据之前的回复
def get_f2( f: Int => Int, x: Int, y: Int) : Int = {
def tail(y: Int, x: Int)(f: Int => Int) : Int = {
x match {
case 0 => y
case _ => tail(f(y), x - 1)(f) : Int
}
}
tail(y, x)(f)
}