zoukankan      html  css  js  c++  java
  • 【转】Scala 中的 Stream

    /////////////////////////////////////
      def numsFrom(n: Int): Stream[Int] = n #:: numsFrom(n + 1)
      
      def testStream = {
        val tenOrMore = numsFrom(10)
        println(tenOrMore)
        println(tenOrMore.tail)
        println(tenOrMore.tail.tail)
        println(tenOrMore.tail.tail.tail)
        
        val squares = numsFrom(1).map { x => x * x }
        println(squares)
        println(squares.take(5).force)
        println(squares take 5 mkString(", "))
        
        val pom = Source.fromFile("pom.xml").getLines().toStream
        println(pom)
        pom.take(10).force.foreach { println }
      }
      /////////////////////////////////////
    
    /*
    output
    
    Stream(10, ?)
    Stream(11, ?)
    Stream(12, ?)
    Stream(13, ?)
    Stream(1, ?)
    Stream(1, 4, 9, 16, 25)
    1, 4, 9, 16, 25
    Stream(<?xml version="1.0"?>, ?)
    <?xml version="1.0"?>
    <project
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    	xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    	<modelVersion>4.0.0</modelVersion>
    	<parent>
    		<groupId>cn.test</groupId>
    		<artifactId>test-parent</artifactId>
    		<version>0.0.1-SNAPSHOT</version>
    	</parent>
    
    */
    

      

    原文链接 http://www.importnew.com/3587.html

     

     

    对于一组值序列的处理,Scala提供了一些功能强大的抽象。函数式编程鼓励使用包含所有状态的声明式列表,而避免使用变量和可变状态。高阶函数,例如map,flatMap和filter可以很简洁地实现复杂的需求。

    例如,假如你在编写一个搜索引擎,并且你的用户需要在一个包含10,0000个HTML文档的数据库里查找关于Scala的第10个文档。在函数式的语言里,你可能会声明一个包含所有文档的列表,然后分别查看每一个文档看看是否是关于Scala,最后获取第10份文档。可能和下面的代码看起来相似:

    1
    documents.filter(isAboutScala)(9)

    这里的问题在于,如果HTML的文档数量有10,000这么多的话,你首先需要一个一个地处理这10,000个文档,来看看它是否是关于Scala的,然后来构建这个过滤了的列表——这仅仅只是为了获取第10个文档。显然这种开销的扩展性不是很好!

    一种解决方案是使用Scala的Stream结构,它和列表相似,只不过它会延迟计算下一个元素,仅当需要的时候才会去计算。使用Stream只会进行必要的处理来查找第10份文档 – 而不是对所有的10,000份文档同时进行计算。

    要查看Stream的详细介绍,你可以参考《Scala By Example》里的“Computing with Streams”。

    那么,Stream究竟是如何起作用的呢?所谓的延迟处理又是怎么定义的呢?深入了解Stream的实现细节,可以发现Scala语言以及构建类库所采纳的一些很好的理念。

    构造一个Stream需要使用Stream.consStream.empty:

    1
    Stream.cons(3, Stream.cons(4, Stream.empty))

    乍一看,cons似乎是Stream对象的一个方法。但是,事实上它是Stream对象的一个内部对象。

    Stream.cons是一个对象,而不是Stream对象的一个方法

    查看Scala类库里的Stream.scala,你会发现下面的结果:

    1
    2
    3
    4
    5
    6
    object Stream {
      object cons {
        // ... definition of Stream.cons object
      }
      // ... rest of Stream
    }

    这种嵌套对象的方式在Java里也是可行的。不过,Scala里使用apply()方法(这个我们后面会讨论)意味着嵌套类的cons的方法调用看起来是Stream对象上的方法调用。

    语法糖(Syntactic sugar)和apply()方法

    回想一下,Scala对象可能会这样定义apply()方法:

    1
    2
    3
    4
    object List {
      def apply[A](xs: A*): List[A] = xs.toList
      // ...
    }

    这就意味着,你可以通过下面的语法来调用List对象创建一个包含1,2和3的列表:

    1
    val usingApply = List.apply(1, 2, 3)

    或者使用Scala的语法糖:

    1
    val usingSugar = List(1, 2, 3)

    对于我而言,apply()的语法糖和嵌套的单例对象的组合很好的诠释了Scala语言的特点。一个方法调用实际上是对一个内部对象的方法调用,不过语法上却没有区别。终端用户不需要了解你是否在操作类库中的一个类的嵌套内部对象;因为它和标准的函数调用没有区别。

    不过,Stream的延迟处理是怎么实现的呢?

    Call by name 求值

    在看一看Stream.cons的apply方法的签名:

    1
    def apply[A](head : A, tail : => Stream[A]) : Stream[A]

    注意=>操作符;它表示一个call-by-name求值。这是参数延迟求值的一种形式;tail参数只会在需要使用的时候才会被求值。

    这样,Scala就可以允许你毫无顾忌地创建一个开销很大的列表;运行时环境会一个一个地对Stream中的元素进行求值,知道指定的条件达到——并且后面的元素不会被求值。

    想了解更多关于Scala的Stream的话,你可以参考这篇文章,它介绍了如何使用Scala解决Euler项目里的问题。

    英文原文: Looking at Streams in Scala,翻译:ImportNew - 朱伟杰

    本文地址:http://www.importnew.com/3587.html

  • 相关阅读:
    Linux中使用dd制作文件的.img
    python正则表达式
    使用@property
    Win10添加删除虚拟打印机方法
    jenkins权限
    RedHat7.2下Jenkins的安装配置
    jenkins忘记管理员账号密码的补救方法
    RHEL软件安装
    docker 常用指令(RHLE)
    /var/run/yum.pid 已被锁定,PID 为 4242 的另一个程序正在运行
  • 原文地址:https://www.cnblogs.com/ihongyan/p/4811761.html
Copyright © 2011-2022 走看看