如何计算 Scala 中 for comprehension 的迭代次数?

How to count the number of iterations in a for comprehension in Scala?

我在流上使用 for comprehension,我想知道需要多少次迭代才能获得最终结果。

在代码中:

var count = 0
for {
   xs <- xs_generator
   x <- xs
   count = count + 1 //doesn't work!!
   if (x prop)
   yield x
}

有办法实现吗?

编辑: 如果您不想 return 只有第一项,而是整个解决方案流,请查看第二部分。

Edit-2: 较短的版本附加了 zipWithIndex

不完全清楚您要做什么。在我看来,您似乎在尝试在列表流中查找某些内容,并另外保存已检查元素的数量。

如果这是您想要的,请考虑这样做:

/** Returns `x` that satisfies predicate `prop` 
  * as well the the total number of tested `x`s
  */
def findTheX(): (Int, Int) = {
  val xs_generator = Stream.from(1).map(a => (1 to a).toList).take(1000)
  var count = 0
  def prop(x: Int): Boolean = x % 317 == 0
  for (xs <- xs_generator; x <- xs) {
    count += 1
    if (prop(x)) {
      return (x, count)
    }
  }
  throw new Exception("No solution exists")
}

println(findTheX())

// prints:
// (317,50403)

几个要点:

  • Scala 的 for-comprehension 与 Python 的 "yield" 没有任何关系。以防万一您认为他们这样做了:re-read for-comprehensions.
  • 上的文档
  • 没有 built-in 语法来中断 for-comprehensions。最好把它包装成一个函数,然后调用return。虽然也有 breakable,但它适用于异常。
  • 函数return是找到的项目和选中项目的总数,因此return类型是(Int, Int)
  • for-comprehension之后最后的错误是确保return类型是Nothing <: (Int, Int)而不是Unit,它不是子类型(Int, Int).
  • 当您想以这种方式将 Stream 用于此类目的时请三思:生成前几个元素后,Stream 会将它们保存在内存中。如果 Stream 使用不当,这可能会导致 "GC-overhead limit exceeded"-错误。

再次强调:Scala for-comprehensions 中的 yield 与 Python 的 yield 无关。 Scala 没有 built-in 对协程和生成器的支持。您并不像您想象的那样经常需要它们,但它需要一些重新调整。


编辑

我又 re-read 你的问题了。如果您想要整个解决方案流以及已检查了多少个不同 x 的计数器,您可以使用类似的东西:

val xs_generator = Stream.from(1).map(a => (1 to a).toList)
var count = 0
def prop(x: Int): Boolean = x % 317 == 0
val xsWithCounter = for {
  xs <- xs_generator; 
  x <- xs
  _ = { count = count + 1 }
  if (prop(x))
} yield (x, count)

println(xsWithCounter.take(10).toList)

// prints:
// List(
//   (317,50403), (317,50721), (317,51040), (317,51360), (317,51681), 
//   (317,52003), (317,52326), (317,52650), (317,52975), (317,53301)
// )

注意 _ = { ... } 部分。 for-理解中可以发生的事情数量有限:

  • 生成器(x <- 事物)
  • filters/guards (if-s)
  • 值定义

在这里,我们 sort-of 滥用 value-definition 语法来更新计数器。我们使用块 { counter += 1 } 作为赋值的右侧。它 returns Unit。因为我们不需要块的结果,所以我们使用 _ 作为赋值的左侧。这样,这个block每x.

执行一次

EDIT-2

如果改变计数器不是您的主要目标,您当然可以直接使用 zipWithIndex

val xsWithCounter = 
  xs_generator.flatten.zipWithIndex.filter{x => prop(x._1)}

它给出的结果与以前的版本几乎相同,但是索引移动了 -1(这是索引,而不是尝试的次数 x-s)。