【发布时间】:2013-03-03 12:49:26
【问题描述】:
假设我想构造一个包含从 0 到 n 的所有整数满足某个谓词 f: Int => Boolean 的 BitSet。
我可以写类似的东西
BitSet((0 until n):_*).filter(f)
这当然有效。但是感觉效率很低!我计划在一个非常紧凑的循环中执行此操作,并希望获得更有效方法的建议。
【问题讨论】:
假设我想构造一个包含从 0 到 n 的所有整数满足某个谓词 f: Int => Boolean 的 BitSet。
我可以写类似的东西
BitSet((0 until n):_*).filter(f)
这当然有效。但是感觉效率很低!我计划在一个非常紧凑的循环中执行此操作,并希望获得更有效方法的建议。
【问题讨论】:
如果性能确实是您最关心的问题,最好的选择可能是使用mutable.BitSet 和while 循环,然后在结果上调用toImmutable。
val bitSet = {
val tmp = new scala.collection.mutable.BitSet(n)
var i = 0;
while (i < n) {
if (f(i)) {
tmp += i
}
i = i + 1
}
tmp.toImmutable
}
【讨论】:
我认为最有效的“功能”方式是使用foldLeft:
(1 to 5).foldLeft(BitSet())((s,i) => if (f(i)) s + i else s)
它不会创建中间集合,而是在过滤时从头开始构建集合。
我首先想到的是使用breakOut,但是对于filter却不行:
scala> val set: BitSet = (0 until 10).filter(f)(collection.breakOut)
<console>:11: error: polymorphic expression cannot be instantiated to expected type;
found : [From, T, To]scala.collection.generic.CanBuildFrom[From,T,To]
required: Int
val set: BitSet = (0 until 10).filter(f)(collection.breakOut)
^
scala> val set: BitSet = (0 until 10).map(_+1)(collection.breakOut)
set: scala.collection.immutable.BitSet = BitSet(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
breakOut 也不会创建中间集合,但是因为filter 没有第二个参数列表,所以它不能工作。
【讨论】:
这是我目前能想到的最好的方法
BitSet((0 until n).view.filter(f):_*)
视图部分使filter 方法变得懒惰。这确保当BitSet 从给定序列创建时,它会即时过滤。您最初的建议在创建第一个 BitSet 后创建一个新的。
【讨论】:
filter 创建了一个中间集合,即使使用view。事实上view在这里完全没用。 : _* 表示直接传递参数,而不是将其包装在一个序列中。
IndexedSeq 中,并作为单个参数传递给方法。换句话说,它们肯定被包裹了。
Range 从 TraversableLike 继承其 filter 实现,在此处定义:github.com/scala/scala/blob/v2.10.1/src/library/scala/…。如您所见,它遍历原始集合的每个元素(它只是foreach),因此即使通过view 使集合变得懒惰,filter 仍将强制评估原始集合的每个元素.因此,在这种情况下,使用 view 实际上可以通过引入不必要的额外间接来使事情变得更糟。