【问题标题】:scala Stream transformation and evaluation modelscala流转换和评估模型
【发布时间】:2015-08-19 06:04:59
【问题描述】:

考虑以下列表转换:

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尤其是这种转化链中的评估模型是什么?

【问题讨论】:

  • 存在一个新的Stream 对象并不意味着整个集合都通过了。

标签: scala stream lazy-evaluation lazy-sequences


【解决方案1】:

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

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

它不会避免每个中间体,但它可能很有用。 This page 有很多信息,值得花时间。

【讨论】:

    【解决方案2】:

    不,您无法通过使用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
    

    【讨论】:

      【解决方案3】:

      据我所知,如果您总是遍历整个集合 Stream 对您没有帮助。 它将创建与新 StreamsList 相同的号码。

      如果我错了,请纠正我,但我理解如下:

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

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

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

      result.foreach(println)
      

      对于每次迭代,都会评估上述链以获得当前项目。

      但是,如果您将过滤器替换为 withFilter,则可以将通过次数减少 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
      }
      

      【讨论】:

        【解决方案4】:

        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 }
        

        但是为了解决您关于 Streams 的问题;是的,可以使用流 对于需要的东西通常很早就发现的大型收藏品, 流是一个不错的选择:

        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
        

        在这个 Stream 示例中,过滤器仅在需要时应用于下一个元素,而不是整个列表 - 好东西,否则它永远不会停止 :)

        【讨论】:

          猜你喜欢
          • 2017-12-28
          • 2014-02-08
          • 1970-01-01
          • 2017-03-24
          • 2020-03-06
          • 2017-04-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多