I hope someone can help me refactor this code to make it work lazily.
I have a tail recursive function which is effectively two concentric flatMap
s. The loop (recursion) proceeds, until such time as some generated element meets a certain condition, found
. My implementation of the function works, but is not ideal. I’d really like to only generate elements until I find a match, and I fear that the flatMap
of flatMap
could in some cases generate very large lists.
Can someone help me refactor this so that it works lazily?
As I see it recure(routings flatMap extendRouting)
iterates over the entire routings
list, and only thereafter checks whether any of the items generated matches the found
predicate. I can’t check the found
predicate inside function extendRouting
because I don’t know how to return from wishToBeLazy
as soon as found
returns true
.
I feel like the call to find
would like iterate across routings
and routings
be generated lazily. If find
finds nothing, then test to see whether an empty list was generated, and if not make the recursive call again. But I don’t know how to do that.
I’d appreciate any clue or suggestion.
object LazyMetro {
type Station = Int
type Time = Int
type Leg = (Station,Station,Time)
case class Routing(path:List[Leg], stations:Set[Station]) {
assert (path != Nil) // I never will consider empty paths
val currentStation = path match {
case leg :: _ => leg._2
}
}
def wishToBeLazy(initial:List[Routing], legData:List[Leg], skip:Leg=>Boolean, found:Routing=>Boolean):Option[Routing] = {
val metroAdj = legData groupBy (_._1)
def extendRouting(routing: Routing): List[Routing] = {
metroAdj(routing.currentStation) flatMap {
leg: Leg => {
leg match {
case leg if skip(leg) => Nil
case (_, dst, _) => List(Routing(leg::routing.path,routing.stations+dst))
}
}
}
}
def recure(routings: List[Routing]): Option[Routing] = {
if (routings == Nil)
None
else {
val augmenting = routings find found
augmenting match {
case Some(_) => augmenting
case None => recure(routings flatMap extendRouting)
}
}
}
recure(initial)
}
}
P.S., I did consider changing extendRouting
to test the found
function on each element generated, return an Either
type indicating the lucky element, or list list of objects, none of which match the predicate. I’m not sure whether that approach is more or less obscure than the lazy approach.