Why is this scala concatenation function not tail recursive?

做~自己de王妃 提交于 2019-12-25 08:00:09

问题


I'm trying to write a function repeat(s: String, n: Int) that will concatenate string s n times and return it, but for some reason I am not getting the correct results and am getting an error that it is not tail recursive, and I'm having trouble grasping logically why this wouldn't be tail recursive.

Does the recursion have to process before the concatenation can be completed? How would I solve the problem? Making the recursion repeat(s+s, n-1) wouldn't work because it would recurse s too many times, but i'm not sure what other way to do it.

/**
 * 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: i'm doing this mostly to practice recursion as opposed to in order to get optimized code


回答1:


In case of tail recursion current result should not depend on deferred previous call stack computation

Make the following change to your function def repeat(s: String, result: String, n: Int): String. Notice result in the function parameters

@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. 
    }

Usage

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

Cleaner way to implement using helper inner function

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)
    }

Usage

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

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



回答2:


The line s + repeat(s, n-1) makes you function answer depended on other calls of the function, if you want to have a tail recursion you should avoid dependence between different calls.

For example:

 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)
  }


来源:https://stackoverflow.com/questions/39603912/why-is-this-scala-concatenation-function-not-tail-recursive

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!