scala流转换和求值模型



考虑以下列表转换:

List(1,2,3,4) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)

按以下方式求值:

List(1, 2, 3, 4) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)
List(11, 12, 13, 14) filter (_ % 2 == 0) map (_ * 3)
List(12, 14) map (_ * 3)
List(36, 42)

所以有三次传递,每一次都创建一个新的列表结构。

那么,第一个问题: Stream可以帮助避免它吗?如果是——如何避免?所有的评估是否可以在一次传递中完成,而不需要创建额外的结构?

下面的流计算模型是否正确:

Stream(1, ?) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)
Stream(11, ?) filter (_ % 2 == 0) map (_ * 3)
// filter condition fail, evaluate the next element
Stream(2, ?) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)
Stream(12, ?) filter (_ % 2 == 0) map (_ * 3)
Stream(12, ?) map (_ * 3)
Stream(36, ?)
// finish

如果是,那么在List的情况下,有相同数量的传递和相同数量的新Stream结构创建。如果不是——那么第二个问题:在这种类型的转换链中,Stream评价模型是什么?

避免中间集合的一种方法是使用view

List(1,2,3,4).view map (_ + 10) filter (_ % 2 == 0) map (_ * 3)

它并没有避免每一个中间部分,但是它是有用的。这个页面有很多信息,非常值得花时间。

不,你不能通过使用Stream来避免它。但是您确实可以通过使用collect方法来避免它,并且您应该保持每次在filter之后使用map时可能需要collect的想法。下面是代码:

scala> def time(n: Int)(call : => Unit): Long = {
     |   val start = System.currentTimeMillis
     |   var cnt = n
     |   while(cnt > 0) {
     |     cnt -= 1
     |     call
     |   }
     |   System.currentTimeMillis - start
     | }
time: (n: Int)(call: => Unit)Long
scala> val xs = List.fill(10000)((math.random * 100).toInt)
xs: List[Int] = List(37, 86, 74, 1, ...)
scala> val ys = Stream(xs :_*)
ys: scala.collection.immutable.Stream[Int] = Stream(37, ?)
scala> time(10000){ xs map (_+10) filter (_%2 == 0) map (_*3) }
res0: Long = 7182
//Note call force to evaluation of the whole stream.
scala> time(10000){ ys map (_+10) filter (_%2 == 0) map (_*3) force } 
res1: Long = 17408
scala> time(10000){ xs.view map (_+10) filter (_%2 == 0) map (_*3) force }
res2: Long = 6322
scala> time(10000){ xs collect { case x if (x+10)%2 == 0 => (x+10)*3 } }
res3: Long = 2339

据我所知,如果您总是遍历整个集合流不会帮助您。它将创建与List相同的新Streams号码。

如果我说错了请指正,但我的理解如下:

Stream是一个惰性结构,所以当你这样做时:

val result = Stream(1, ?) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)

的结果是另一个流,它链接到前面转换的结果。因此,如果强制评估与foreach(或例如mkString)

result.foreach(println)

对于每次迭代,上面的链都求值以获得当前项。

但是,如果用withFilter替换filter,则可以减少1次通过。然后过滤器被应用于map函数。

List(1,2,3,4) map (_ + 10) withFilter (_ % 2 == 0) map (_ * 3)

您可以使用flatMap:

将其减少为一次传递
List(1,2,3,4) flatMap { x =>
  val y = x + 10
  if (y % 2 == 0) Some(y * 3) else None
}

Scala可以通过多种方式对集合进行过滤和转换。

第一个例子:

List(1,2,3,4) map (_ + 10) filter (_ % 2 == 0) map (_ * 3)

可优化:

List(1,2,3,4) filter (_ % 2 == 0) map (v => (v+10)*3)

或者,可以使用折叠:

List(1,2,3,4).foldLeft(List[Int]()){ case (a,b) if b % 2 == 0 => a ++ List((b+10)*3) case (a,b) => a }

或者,可能是for表达式:

for( v <- List(1,2,3,4); w=v+10 if w % 2 == 0 ) yield w*3

或者,也许是最容易理解的,一个集合:

List(1,2,3,4).collect{ case v if v % 2 == 0 => (v+10)*3 }

但是为了解决你关于流的问题;是的,可以使用流对于大型收藏品来说,想要的东西往往很早就找到了流是一个不错的选择:

def myStream( s:Stream[Int] ): Stream[Int] = 
  ((s.head+10)*3) #:: myStream(s.tail.filter( _ % 2 == 0 ))
myStream(Stream.from(2)).take(2).toList  // An infinitely long list yields
                                         //   36 & 42 where the 3rd element
                                         //   has not been processed yet

在这个流示例中,过滤器只应用于需要的下一个元素,而不是整个列表——很好,否则它永远不会停止:)

相关内容

  • 没有找到相关文章