Reputation: 281
I want to make the following recursive method tail-recursive, but I am not sure if this is even possible, since the method is calling itself inside a flatMap
.
@tailrec
def loop(nodes: List[Node], myNodes: List[MyNode]): List[MyNode] = nodes match {
case Nil => myNodes
case _ =>
nodes.flatMap { n =>
val myNode = MyNode(data = n.data)
loop(n.getChildNodes().toList, myNode :: myNodes)
}
}
loop(nodes, List())
Unfortunately, I get the following error:
could not optimize @tailrec annotated method loop: it contains a recursive call not in tail position
[error] case _ =>
[error] ^
[error] one error found
Upvotes: 0
Views: 403
Reputation: 5315
As @SteffenSchmitz said, you cannot make the recursive call inside a flatMap
. Indeed, in the case where the list has at least two Node
s, your method will have at least two calls to itself, so it cannot be tailrec. However, you can do the same that your logic is doing here with tail-recursion.
What your code is doing, is copying data in Node
s in nodes
which seems to be sone kind of tree structure into a List[MyNode]
.
In the end, you get the list of all the ancestors for each of the leaves of the original tree.
For instance, if your tree is
A
/ \
B C
/ \
D E
You'll get List(B, A, D, C, A, E, C, A)
(here, I'm assuming data
is a Char
, for simplicity). The first B, A
are the ancestors for B
, the next D, C, A
are the ancestors for D
, and the last ones for E
.
You can do the same with the following tailrec:
@tailrec
def tailrecLoop(nodesWithAncestors: List[(Node, List[MyNode])], acc: List[MyNode]): List[MyNode] = {
nodesWithAncestors.headOption match {
case None => acc
case Some((node, ancestors)) if node.getChildNodes().toList.isEmpty =>
loop(nodes.tail, acc ::: myNode :: ancestors)
case Some((node, ancestors)) =>
val myNode = MyNode(data = node.data)
val childrenWithAncestors =
node.getChildNodes().toList.map(_ -> (myNode :: ancestors))
loop(childrenWithAncestors ++ nodes.tail, acc)
}
}
def loop(nodes: List[Node]) = tailrecLoop(nodes.map(_ -> Nil), Nil)
The key here is to put the non-treated nodes (nodesWithAncestors
) in a queue, to be handled at some later time by sub-calls.
Upvotes: 4
Reputation: 890
It's not possible to make a call inside a flatmap tail-recursive. The last call in your function has to be loop().
Upvotes: 1