当前位置: 首页 > 知识库问答 >
问题:

为什么这个scala连接函数不是尾部递归的?

别开诚
2023-03-14

我试图编写一个函数repeat(s:String,n:Int),它将串接字符串s n次并返回它,但由于某种原因,我没有得到正确的结果,并且得到了一个错误,即它不是尾部递归的,我在逻辑上很难理解为什么它不是尾部递归的。

在连接完成之前,是否必须处理递归?我该如何解决这个问题?使递归重复(s,n-1)不起作用,因为它会递归s太多次,但我不确定还有什么其他方法可以做到。

/**
 * Returns concatenated string s, concatenated n times
 */
@annotation.tailrec
def repeat(s: String, n: Int): String = n match {
  case zero if (n == 0) => "" // zero repeats 
  case emptyString if (s == "") => throw new IllegalArgumentException("You must input a string") //no string exception
  case fail if (n < 0) => throw new IllegalArgumentException("Cannot use a negative number") //throw exception
  case last if (n == 1) => s
  case concatenate => s + repeat(s, n-1) //tail-end recursion & concat. 
}

ps:我这么做主要是为了练习递归,而不是为了得到优化的代码

共有2个答案

时浩波
2023-03-14

在尾部递归的情况下,当前结果不应依赖于延迟的先前调用堆栈计算

对函数def repeat(s:String,result:String,n:Int):String进行以下更改。注意函数参数中的结果

@annotation.tailrec
    def repeat(s: String, result: String, n: Int): String = n match {
      case zero if (n == 0) => "" // zero repeats 
      case emptyString if (s == "") => throw new IllegalArgumentException("You must input a string") //no string exception
      case fail if (n < 0) => throw new IllegalArgumentException("Cannot use a negative number") //throw exception
      case last if (n == 1) => result
      case concatenate => repeat(s, s + result, n-1) //tail-end recursion & concat. 
    }

用法

scala> repeat("apple", "apple", 2)
res3: String = appleapple

使用helper内部函数实现更简洁的方法

def repeat(s: String, n: Int): String = {
      @annotation.tailrec
      def helper(result: String, n: Int): String = n match {
        case zero if (n == 0) => "" // zero repeats
        case emptyString if (s == "") => throw new IllegalArgumentException("You must input a string") //no string exception
        case fail if (n < 0) => throw new IllegalArgumentException("Cannot use a negative number") //throw exception
        case last if (n == 1) => result
        case concatenate => helper(s + result, n - 1) //tail-end recursion & concat.
      }
      helper(s, n)
    }

用法

scala> repeat("apple", 1)
res6: String = apple

scala> repeat("apple", 2)
res7: String = appleapple
公良昕
2023-03-14

这一行重复(s,n-1)使函数的答案取决于函数的其他调用,如果您想要有一个尾部递归,您应该避免不同调用之间的依赖。

例如:

 def repeat(s: String, n: Int): String = {

    @annotation.tailrec
    def repeatIter(buffer: String, n: Int): String = {
      n match {
        case 0 => buffer
        case _ => repeatIter(buffer + s, n - 1)
      }
    }

    if (s.isEmpty || n < 0) throw new IllegalArgumentException("ERROR")
    else repeatIter("", n)
  }
 类似资料:
  • 我有一个(co?)递归函数对,它们处理元组列表,并根据一些开始和结束条件将它们折叠成批处理。 我做得不多,所以我可能很愚蠢。 我已经修改了一个简单的非尾部递归版本,通过明确引入一个“tot”参数来构成当前折叠状态,我认为这是尾部递归的,但我在大输入上得到了可怕的堆栈溢出。。。。(在调试器和(调试)中)。exe) 作为一个明确的折叠,可能有更好的方法来做到这一点...但这几乎不是重点,重点是为什么它

  • 我仍在尝试实现2-3个手指树,并取得了良好的进展(存储库)。在做一些基准测试时,我发现当树非常大时,我非常基本的toList会导致堆栈溢出异常。起初,我看到了一个简单的修复方法,并将其设置为尾部递归。 不幸的是,事实证明,toList不是罪魁祸首,但viewr是: 寻找唯一的递归调用很明显,这不是尾部递归。不知何故,我希望这个问题不会存在,因为这个调用被包装在一个类似于连续的延迟中。 我听说并读过

  • 假设我编写这样的代码: 我如何让Kotlin优化这些相互递归的函数,以便在不抛出StackOverflower错误的情况下运行main?tailrec关键字适用于单函数递归,但没有更复杂的功能。我还看到一个警告,在使用关键字tailrec的地方没有找到尾部调用。也许这对编译器来说太难了?

  • 我想我理解了教科书中对尾部递归函数的定义:在函数调用后不执行任何计算的函数。我还发现,作为一个结果,尾部递归函数的内存效率会更高,因为它每次调用只需要一条记录,而不是每次都需要保留一条记录(就像在普通递归中那样)。 我不太清楚的是,这个定义如何应用于嵌套调用。我将提供一个例子: 我最初给出的答案是,根据定义,它不是尾部递归的(因为外部调用是在计算内部调用之后执行的,所以其他计算是在第一次调用之后完

  • 我在构建递归函数时继续遇到一个问题,其中返回的值与我期望返回的值不同。我很确定这与函数的递归性质有关,但我不知道发生了什么。 在这个缩小的例子中,我有一个带有字符串的函数foo和一个默认值为0的int。给定字符串“测试”并且没有整数,我希望递归函数为每个调用增加numberToBack并将新值传递给下一个调用。这一定是部分正确的,因为如果我在到达基本情况时cout numberToBack,我将获

  • 我有一个家庭作业问题,它给出了一个递归函数,我必须使用尾部递归来实现它。函数为f(0)=1 f(n)=1 2*f(n-1) 我并不擅长尾部递归,我试着查找示例,但我发现的都是没有斐波那契序列的示例,这没有多大帮助。 我真正拥有的是 我知道尾递归基本上每次调用都计算函数,我只是不知道如何实现它。 编辑:我打了一个错字f(n)应该是1 2*f(n-1)