【问题标题】:How to cut a for-comprehension short (break out of it) in scala?如何在scala中缩短理解力(突破它)?
【发布时间】:2012-11-28 01:38:19
【问题描述】:

我有一段代码如下:

val e2 = for (e <- elements if condition(expensiveFunction(e))) yield {
            expensiveFunction(e)
         }

条件对少数元素为真,然后对所有其余元素为假。

不幸的是,这不起作用(即使我忽略了性能),因为我的 elements 是一个无限迭代器。

有没有办法在理解中使用“中断”,以便在满足特定条件时停止产生元素?否则,计算我的 e2 的 scala 惯用方法是什么?

【问题讨论】:

  • 这个问题是关于一个常规循环,而不是一个 for-comprehension(即,有产量)。我怀疑 takeWhile 可以成为解决方案的一部分......
  • 问题有所不同,但答案(据我所知)也可以应用于您的情况
  • for-comprehensions 与传统的循环完全不同。它们共享相似语法的事实是使它们更易于阅读。

标签: scala break for-comprehension infinite-sequence


【解决方案1】:

你可以采用懒惰的方法:

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

所以你按需计算昂贵的Function,如果某个步骤有错误,你就不会做不必要的工作。

【讨论】:

  • +1 -- 不过,因为我的特殊情况更清楚地写成便于理解,所以我接受了 som-snytt 的回答。谢谢。
  • 这实际上可能是 最佳 答案:来自@som-snytt 的答案为所有元素调用expensiveFunction - 并且只有之后 丢弃不需要的结果。
  • @javadba 不,用于理解去糖映射。我想我的答案是关于for?否则它们是相同的。
  • 我以礼貌的方式使用了可能,但实际上我已经测试过验证你的方法并不贪婪而来自@som-snytt 的内容确实会产生 expensiveFunction 的全部成本,即使在未使用/不需要的元素上也是如此。
  • 最近关于惰性/严格的 cmets 很有趣,因为根据问题,elements 已经是一个迭代器。
【解决方案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)

编辑,另一个演示:

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)

【讨论】:

  • 用我的术语来说,就是(for(e &lt;- elements) yield expensiveFunction(e)).takeWhile(r =&gt; condition(r))。我很惊讶它是如此明显。看起来关于无限迭代器的推理对我来说仍然很困难。
  • @jsalvata note (for(...)...) takeWhile 条件。占位符语法,由于预期类型而省略占位符。并失去点和括号。其他人让我迷上了极简主义。
  • 这似乎为 所有 元素计算 expensiveFunction - 并且仅在计算后 截断结果序列。
  • @javadba 你观察到什么暗示了这一点?比较 Stream.range(0,20).map(compute).toListStream.range(0,20).map(compute).takeWhile(_ &lt; 100).toList。每次从左到右操作后都没有实现集合。
  • @javadba 它取决于使用的集合。对于严格集合,它将计算所有元素,对于惰性集合则不会。
【解决方案3】:

你可以只使用 takeWhile:

elements.takeWhile(condition(expensiveFunction(_)))

【讨论】:

  • 我可以使用elements.takeWhile(e =&gt; condition(expensiveFunction(e)),但是我需要再次为它们中的每一个重新计算昂贵的函数。
【解决方案4】:

找到了这个解决方案:

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

更好的,有人吗?

【讨论】:

    【解决方案5】:

    这是我的想法: 如果元素已经是惰性容器(如 Stream、Iterator):

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

    或者不:

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

    【讨论】:

      猜你喜欢
      • 2010-12-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-16
      • 2013-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多