Вопрос

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?

Это было полезно?

Решение 2

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(_)))

Found this solution:

(for (e <- elements) yield {
  val x= expensiveFunction(e)
  if (condition(x)) Some(x) else None
}).takeWhile(_.nonEmpty).map(_.get)

A better one, anyone?

Here is my idea: If the elements is a lazy container already(like Stream, Iterator):

(for (e <- elements; 
      buf = expensiveFunction(e);  
      if condition(buf)) yield buf).headOption

Or not:

(for (e <- elements.view; 
      buf = expensiveFunction(e);  
      if condition(buf)) yield buf).headOption
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top