Reputation: 1406
Suppose that I have some Tree
class which internally uses Node
s:
class Tree {
val root = Node(null)
case class Node(parent: Node) {
val children: List[Node]
}
}
now suppose that I want to get all ancestors of some node applying some filter function. The obvious way to do it is something like this:
def getAncsWithFilter(filter: (Node) => Boolean): List[Node] {
var curNode = this.parent
var res: List[Node] = Nil
while (curNode != null) {
if (filter(curNode)) {
res = res :: curNode
}
curNode = curNode.parent
}
res
}
What I don't like in this solution is that I use var
s and this code seems too imperative and ugly. What I want to be able to write is something like this
def getAncsWithFilter(filter: (Node) => Boolean): List[Node] {
val curNode = this
for (curNode <- curNode.parent if filter(curNode))
yield curNode
}
Is it possible is Scala? Currently, if I write code like this, it will generate some error complaining about absence of withFilter
method. If I extend my Node
class with Traversable[Node]
then code above will return Traversable
instead of List.
Upvotes: 2
Views: 522
Reputation: 1492
A for-comprehension
would require an implementation of flatMap
on Node
. Another option is to use recursion:
def getAncsWithFilter(filter: (Node) => Boolean, ancs: List[Node] = List()): List[Node] = {
(parent, filter(parent)) match {
case (null, _) => ancs
case (_, true) => parent.getAncsWithFilter(filter, parent :: ancs)
case (_, false) => parent.getAncsWithFilter(filter, ancs)
}
}
Upvotes: 0
Reputation: 14224
The problem here is that curNode.parent
returns a single Node
, not a sequence of all ancestors, so you are not iterating over anything.
You can implement a separate method ancestors
, that would return a sequence of all ancestors and you would be able to iterate through it.
A quick way to get something iterable with ancestors, is to use Iterator.iterate
:
def ancestors: Iterator[Node] =
Iterator.iterate(this.parent)(_.parent).takeWhile(_ != null)
The full implementation of getAncsWithFilter
will then become:
def getAncsWithFilter(filter: (Node) => Boolean): List[Node] =
ancestors.filter(filter).toList
You don't even have to use a for-comprehension (you can still use it of course, but in that case the code will become more complex in my opinion).
Upvotes: 4