【转】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

时间: 2024-10-22 05:35:28

【转】Scala 中的 Stream的相关文章

play scala 1 用stream表示全体自然数

1.在创建一个List的时候,tail是立即被evaluated,如果把构造函数中的tail改成call by name的参数,而可以在用到tail的时候才计算tail.stream的实现就是用了这个思想 Steam的简单实现   def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {     def isEmpty = false     def head = hd     def tail = tl   }   val empty = n

java 8 中的stream

Java 8 中的 Streams 详解 Java 8 是迄今为止在语义上改动上最大的一个平台.除了最显著的 Lambda 表达式之外,还有很多初次见面的特性,例如本文主题的 Streams API.这里介绍了它出现的背景和具体用法. 为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX 对 XML 解析的 Stream,也不是 Amazon Kinesi

Scala中的集合:Iterator、BitSet、Set、Map、Stack、Vector、List、Array

 5.   util包 5.1.     架构 http://www.scala-lang.org/docu/files/collections-api/collections.html The following figure shows all collections in package scala.collection. These are all high-level abstract classes or traits, which generally have mutable

scala中trait学习笔记

scala中提供的trait(特质)和Java中的Interface有很多相似之处.都可以持有方法的声明和属性,但是trait还有比interface强大的多的其他用法. 1. trait可以带有方法实现: 2. trait与interface一样,可以互相继承.但是trait可以继承自某个类,但是这种特质只能够混入父类的子类中,不能随意混入: 3. trait中可以在运行时动态调用方法. 下面举一个trait使用的例子. 首先定义一个虚类IntQueue和特质Logger abstract c

转载: scala中span和partition区别

scala中的partition span splitAt groupBy 可把Collection分成:满足条件的一组,其他的另一组. partitionspan List(1,9,2,4,5).span(_<3)       // (List(1),List(9, 2, 4, 5)),碰到不符合就结束 List(1,9,2,4,5).partition(_<3) // (List(1, 2),List(9, 4, 5)),扫描所有 splitAt // (List(1, 3),List(5

scala学习手记16 &ndash; scala中的static

前面两节学了scala的对象和伴生对象,这两个在使用的时候很有些java的静态成员的意思. scala中没有静态字段和静态方法.静态成员会破坏scala所支持的完整的面向对象模型.不过可以通过伴生对象实现对scala的类一级的操作. 回过头来再看一遍那个Marker的例子,略做了一些调整: class Marker private(val color: String) { println("Creating " + this) override def toString(): Stri

scala学习手记2 - scala中的循环

先来看一段Java中的循环: for (int i = 1; i < 4; i++) { System.out.print(i + ","); } 毫无疑问,scala可以让这个循环更加简洁.根据上一节中的内容,没有必要显示指定变量i的类型,我们甚至不需要声明这个变量.其次输出的语句也可以更加简洁一些,在scala中可以直接使用println()这个方法输出字符串.最后scala的循环结构也是非常的轻量级.好了,可以看一下代码了: for (i <- 1 to 3) { p

第85讲:Scala中For表达式的强大表现力实战

今日[DT大数据梦工厂视频]<第85讲:Scala中For表达式的强大表现力实战>51CTO视频:http://edu.51cto.com/lesson/id-71503.html(DT大数据梦工厂scala的所有视频.PPT和代码在百度云盘的链接:http://url.cn/fSFPjS)85讲 scala for 表达式的强大表现力高阶函数的行为 指定了对数据 处理 的细节 .case class Person(name:String,isMale:Boolean,children:Per

第81讲:Scala中List的构造和类型约束逆变、协变、下界详解

今天来学习一下scala中List的构造和类型约束等内容. 让我们来看一下代码 package scala.learn /** * @author zhang */abstract class Big_Data class Hadoop extends Big_Dataclass Spark extends Big_Data object List_Constructor {  def main(args:Array[String]){    val hadoop = new Hadoop ::