我有以下内容:
case class Node(parent: Option[Node], etc:Any)
@tailrec
def getAncestor(numGens:Int, node:Node): Option[Node] =
if(numGens <= 0) Some(node)
else node.parent match {
case Some(parent) => getAncestor(numGens-1, parent)
case None => None
}
我不喜欢case None => None
。它给我的印象是应该有一个更优雅的方式。但是,如果我将else
替换为:
else node.parent.flatMap(p => getAncestor(numGens-1, p))
然后该函数不再是尾递归的。是否有更惯用的方式来编写这个函数?
答案 0 :(得分:2)
我认为没有办法。你的方法已经足够简洁了。以下是另外两个选项:
// 1. changing argument type
@tailrec
def getAncestor(numGens:Int, node: Option[Node]): Option[Node] =
if(numGens <= 0) node else getAncestor(numGens -1, node.flatMap(_.parent))
// 2. removing if
@tailrec
def getAncestor(numGens:Int, node:Node): Option[Node] =
node.parent match {
case _ if numGens <= 0 => Some(node)
case Some(parent) => getAncestor(numGens-1, parent)
case _ => None
}
答案 1 :(得分:0)
@tailrec
def getAncestor(numGens:Int, node:Node): Option[Node] = {
node.parent match {
case Some(parent) if numGens > 0 => getAncestor(numGens - 1, parent)
case Some(_) | None if numGens == 0 => Some(node)
case _ => None
}
}
答案 2 :(得分:0)
此案例类似乎不太有用。由于每个节点只有一个父节点,因此您所做的就是在没有任何有用方法的情况下重新创建List[Any]
。
如果您将etc
个对象放入列表中,则getAncestor(i, n)
只会变为
ns drop i
结果将是一个较短的列表,顶部有一个所需的节点或一个空列表。因为Scala的不可变列表是persistent data structure,所以不会浪费任何内存。您可以使用
检索新头节点的内容ns.drop(i).headOption
没有堆栈溢出,没有因为尝试使递归函数尾递归而造成的丑陋(有用的显式递归函数在尾递归时几乎总是难看)。更加惯用的Scala,因为它正在使用正确的类型。
如果 尝试使用您的节点构建一些更复杂的结构,那么您最好使用扩展密封特征(或密封的抽象基础)的案例类来构建正确的代数数据类型类)。但是,在扩展这个想法之前,需要有关你的意图的更多信息。