Reputation: 28981
What is a nicest way to implement such function
case class Node(prev: Option[Node])
def flatIt(n: Node): List[Node] = ???
val n1 = Node(None)
val n2 = Node(Some(n1))
val n3 = Node(Some(n2))
flatIt(n3) shouldBe List(n3, n2, n1)
I did it as
def flatIt(n: Node): List[Node] = n :: (n.prev match {
case None => Nil
case Some(prev) => flatIt(prev)
})
but it does not look simple and is not tail-recursive. Any better ideas?
Update:
In fact... I don't need to have the List
specifically. I need a way to wrap the linked list as a simple List-like trait with find/take/map/etc methods. Any nice solution?
Upvotes: 0
Views: 121
Reputation: 7604
Edit: What @jwvh suggested is probably the best idea. Your nodes are already like a linked list, and by creating a List[Node]
, you're just unnecessarily creating extra references. Full disclaimer: I shamelessly copied this from this page. This is not a perfect solution, and you may need to (heavily) modify it
case class Node(prev: Option[Node])
extends Iterable[Node]
with IterableOps[Node, Iterable, Iterable[Node]]
with IterableFactoryDefaults[Node, Iterable]
with StrictOptimizedIterableOps[Node, Iterable, Iterable[Node]] {
def iterator: Iterator[Node] = new NodeIterator(this)
}
class NodeIterator(n: Node) extends Iterator[Node] {
private var curr: Node = n
def hasNext: Boolean = curr.prev != None
def next() = {
val n = curr
curr = curr.prev.get
n
}
}
Here's a tail recursive method, though I'm not sure it's what you want. You'll have to reverse afterwards.
def flatIt(n: Node): List[Node] = {
@annotation.tailrec
def flat(o: Option[Node], curr: List[Node]): List[Node] = o match {
case None => curr
case Some(n) => flat(n.prev, n :: curr)
}
flat(Some(n), Nil).reverse
}
You might prefer something else for simplicity, though. Tail-recursion probably doesn't matter in this case
def flatIt(n: Node): List[Node] = n :: n.prev.map(flatIt).getOrElse(Nil)
or
def flatIt(n: Node): List[Node] = List.unfold(Option(n))(_.map(x => (x, x.prev)))
Upvotes: 3
Reputation: 1095
Your solution is fine as it is, but if you want it tail rec you could do this:
def flatIt(n: Node): List[Node] = {
@tailrec
def acc(n: Node, list: List[Node]): List[Node] = {
n.prev match {
case Some(node) => acc(node, list :+ n)
case None => list :+ n
}
}
acc(n, Nil)
}
Upvotes: 2