I have a piece of code which would code as follows:
val e2 = for (e <- elements if condition(expensiveFunction(e))) yield {
expensiveFunction(e)
}
Where the condition will be true for a few elements and then become false for all remaining ones.
Unfortunately this doesn't work (even if I ignore performance) because my elements
is an infinite iterator.
Is there a way to use a "break" in a for-comprehension so it stops yielding elements when a certain condition holds? Otherwise, what would be the scala-idiomatic way to compute my e2
?
scala> def compute(i: Int) = { println(s"f$i"); 10*i }
compute: (i: Int)Int
scala> for (x <- Stream range (0, 20)) yield compute(x)
f0
res0: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res0 takeWhile (_ < 100)
res1: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res1.toList
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
res2: List[Int] = List(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
Edit, another demonstration:
scala> def compute(i: Int) = { println(s"f$i"); 10*i }
compute: (i: Int)Int
scala> for (x <- Stream range (0, 20)) yield compute(x)
f0
res0: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res0 takeWhile (_ < 100)
res1: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res1.toList
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
res2: List[Int] = List(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
scala> Stream.range(0,20).map(compute).toList
f0
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
f11
f12
f13
f14
f15
f16
f17
f18
f19
res3: List[Int] = List(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190)
scala> Stream.range(0,20).map(compute).takeWhile(_ < 100).toList
f0
f1
f2
f3
f4
f5
f6
f7
f8
f9
f10
res4: List[Int] = List(0, 10, 20, 30, 40, 50, 60, 70, 80, 90)
You could go with lazy approach:
val e2 = elements.toIterator
.map(expensiveFunction)
.takeWhile(result => result == true) // or just .takeWhile(identity)
// you may want to strict iterator, (e.g. by calling .toList) at the end
So you compute expensiveFunction on demand, and if there is false on some step, you won't do unnecessary work.
You can just use takeWhile:
elements.takeWhile(condition(expensiveFunction(_)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With