我还是Scala的新手,我读到的其中一件事就是for-comprehension在某种程度上相当于flatMap。但是,在我的代码(下面)中,flatMap的计算时间至少是两倍。这可能是什么原因?
这是缓慢的:
facts.flatMap(f => factActionsMap(f)).filter(_.isValid(facts))
这是快速等效的:
for {
f <- facts
a <- factActionsMap(f)
if a.isValid(facts)
} yield a
factActionsMap
是Fact
和Set[Action]
之间的地图。
facts
只是Set[Fact]
。
答案 0 :(得分:7)
让我们检查翻译:
scala> trait Fact
defined trait Fact
scala> trait Action { def isValid(s: Set[Fact]): Boolean }
defined trait Action
scala> def facts: Set[Fact] = ???
facts: Set[Fact]
scala> def factActionsMap: Map[Fact, Set[Action]] = ???
factActionsMap: Map[Fact,Set[Action]]
scala> import scala.reflect.runtime.{universe => u}
import scala.reflect.runtime.{universe=>u}
scala> val expr = u reify {
| for {
| f <- facts
| a <- factActionsMap(f)
| if a.isValid(facts)
| } yield a
| }
expr: reflect.runtime.universe.Expr[scala.collection.immutable.Set[Action]] = Expr[scala.collection.immutable.Set[Action]]($read.f
acts.flatMap(((f) => $read.factActionsMap.apply(f).withFilter(((a) => a.isValid($read.facts))).map(((a) => a))(Set.canBuildFrom)))
(Set.canBuildFrom))
scala> u show expr.tree
res0: String = $read.facts.flatMap(((f) => $read.factActionsMap.apply(f).withFilter(((a) => a.isValid($read.facts))).map(((a) => a
))(Set.canBuildFrom)))(Set.canBuildFrom)
因此,删除REPL内容(以$
开头的所有内容)和隐式参数,再加上重新格式化,我们得到:
facts.flatMap(f => factActionsMap(f).withFilter(a => a.isValid(facts)).map(a => a))
这与您提出的内容有两个主要区别。首先,withFilter
应用于fastActionsMap(f)
结果,而您应用于facts.flatMap
结果。这意味着flatMap
将对所有结果起作用,而不仅仅是接受的结果。
其次,它使用withFilter
代替filter
,这可以避免创建额外的集合。
答案 1 :(得分:1)
除非我弄错了,否则与第二个表达式相比更精确的是
facts.flatMap(f => factActionsMap(f).withFilter(_.isValid(facts)))
withFilter
就像过滤器,除了它是懒惰的,所以如果你没有对Set的内容做任何事情,你实际上没有应用过滤器。例如,您可以在第二次调用System.currentTimeMillis之前打印该集的所有元素来检查此假设。