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