如何制作一个在flatMap中调用自身的尾递归方法?

时间:2017-05-28 20:45:00

标签: scala recursion

我想使下面的递归方法是尾递归的,但我不确定这是否可行,因为该方法在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())

不幸的是,我收到以下错误:

could not optimize @tailrec annotated method loop: it contains a recursive call not in tail position
[error]           case _ =>
[error]                  ^
[error] one error found

2 个答案:

答案 0 :(得分:4)

正如@SteffenSchmitz所说,你无法在flatMap内进行递归调用。实际上,在列表至少有两个Node s的情况下,您的方法将至少有两次调用自身,因此它不能是tailrec。但是,您可以使用尾递归来执行与逻辑相同的操作。

您的代码正在做什么,就是将Nodenodes中的数据复制到List[MyNode]中,这似乎是一种树形结构。{/ p>

最后,您将获得原始树的每个叶子的所有祖先的列表。

例如,如果你的树是

  A
 / \
B   C
   / \
  D   E

您将获得List(B, A, D, C, A, E, C, A)(此处,我假设dataChar,为简单起见)。第一个B, AB的祖先,下一个D, C, AD的祖先,E的最后一个。

您可以使用以下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)

这里的关键是将未处理的节点(nodesWithAncestors)放入队列中,稍后通过子调用处理。

答案 1 :(得分:1)

无法在flatmap中进行尾递归调用。函数中的最后一个调用必须是loop()。