Scalaz(46)- scalaz-stream 基础介绍
scalaz-stream是一个泛函数据流配件库(functional stream combinator library),特别适用于函数式编程。scalar-stream是由一个以上各种状态的Process串联组成。stream代表一连串的元素,可能是自动产生或者由外部的源头输入,如:一连串鼠标位置;文件中的文字行;数据库记录;又或者一连串的HTTP请求等。Process就是stream转换器(transducer),它可以把一种stream转换成另一种stream。Process的类型款式如下:
sealed trait Process[+F[_], +O]
其中F是个高阶类,是一种算法,O是Process的运算值。从类型款式上看Process是个对O类型值进行F运算的节点,那么scalaz-stream就应该是个运算流了。Process包含以下几种状态:
case class Emit[+O](seq: Seq[O]) extends HaltEmitOrAwait[Nothing, O] with EmitOrAwait[Nothing, O] case class Await[+F[_], A, +O](
req: F[A]
, rcv: (EarlyCause \/ A) => Trampoline[Process[F, O]] @uncheckedVariance
, preempt : A => Trampoline[Process[F,Nothing]] @uncheckedVariance = (_:A) => Trampoline.delay(halt:Process[F,Nothing])
) extends HaltEmitOrAwait[F, O] with EmitOrAwait[F, O] {
...
}
case class Halt(cause: Cause) extends HaltEmitOrAwait[Nothing, Nothing] with HaltOrStep[Nothing, Nothing] case class Append[+F[_], +O](
head: HaltEmitOrAwait[F, O]
, stack: Vector[Cause => Trampoline[Process[F, O]]] @uncheckedVariance
) extends Process[F, O] {
...
}
scalaz-stream是个主动读取模式的流(pull model stream),Process转换stream的方式不是以Stream[I] => Stream[O]这种函数方式,而是一种状态转换方式进行(state transition),所以这些状态就等于向一个驱动程序发出的请求:
Emit[+O]:请求发一个O值
Await[+F[_],A,+O]:要求运算F[A],得出F[A]的结果A后输入函数rcv再运算得出下一个Process状态。这个是flatMap函数的结构化版本
Halt:停止发送
Append:连接前后两个Process
可以看到Emit,Await,Halt,Append都是Process类型的结构化状态。其中Await就是flatMap函数的结构化,Emit就像Return,所以Process就是一个Free Monad。
Emit的作用是发出一个O值,Await的作用是运算F然后连接下一个Process, Append的作用则是把前一个Process的信息传递到下一个Process。Await和Append分别是不同方式的Process连接方式。
Process又分以下几类:
type Process0[+O] = Process[Nothing,O] /**
* A single input stream transducer. Accepts input of type `I`,
* and emits values of type `O`.
*/
type Process1[-I,+O] = Process[Env[I,Any]#Is, O] /**
* A stream transducer that can read from one of two inputs,
* the 'left' (of type `I`) or the 'right' (of type `I2`).
* `Process1[I,O] <: Tee[I,I2,O]`.
*/
type Tee[-I,-I2,+O] = Process[Env[I,I2]#T, O] /**
* A stream transducer that can read from one of two inputs,
* non-deterministically.
*/
type Wye[-I,-I2,+O] = Process[Env[I,I2]#Y, O] /**
* An effectful sink, to which we can send values. Modeled
* as a source of effectful functions.
*/
type Sink[+F[_],-O] = Process[F, O => F[Unit]] /**
* An effectful channel, to which we can send values and
* get back responses. Modeled as a source of effectful
* functions.
*/
type Channel[+F[_],-I,O] = Process[F, I => F[O]]
Process[F[_],O]:source:运算流源点,由此发送F[O]运算
Process0[+O]:>>>Process[Nothing,+O]:source:纯数据流源点,发送O类型元素
Process1[-I,+O]:一对一的数据转换节点:接收一个I类型输入,经过处理转换成O类型数据输出
Tee[-I1,-I2,+O]:二对一的有序输入数据转换节点:从左右两边一左一右有顺接受I1,I2类型输入后转换成O类型数据输出
Wye[-I1,-I2,+O]:二对一的无序输入数据转换节点:不按左右顺序,按上游数据发送情况接受I1,I2类型输入后转换成O类型数据输出
Sink[+F[_],-O]:运算终点,在此对O类型数据进行F运算,不返回值:O => F[Unit]
Channel[+F[_],-I,O]:运算终点,接受I类型输入,进行F运算后返回F[O]:I => F[O]
以下是一些简单的Process构建方法:
Process.emit() //> res0: scalaz.stream.Process0[Int] = Emit(Vector(1))
Process.emitAll(Seq(,,)) //> res1: scalaz.stream.Process0[Int] = Emit(List(1, 2, 3))
Process.halt //> res2: scalaz.stream.Process0[Nothing] = Halt(End)
Process.range(,,) //> res3: scalaz.stream.Process0[Int] = Append(Halt(End),Vector(<function1>))
这些是纯数据流的构建方法。scalaz-stream通常把Task作为F运算,下面是Task运算流的构建或者转换方法:
val p: Process[Task,Int] = Process.emitAll(Seq(,,)) //> p : scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>))
Process.range(,,).toSource //> res4: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>))
//把F[A]升格成Process[F,A]
Process.eval(Task.delay { * }) //> res5: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Await(scalaz.concurrent.Task@56aac163,<function1>,<function1>)
对stream的Process进行运算有下面几种run方法:
/**
* Collect the outputs of this `Process[F,O]` into a Monoid `B`, given a `Monad[F]` in
* which we can catch exceptions. This function is not tail recursive and
* relies on the `Monad[F]` to ensure stack safety.
*/
final def runFoldMap[F2[x] >: F[x], B](f: O => B)(implicit F: Monad[F2], C: Catchable[F2], B: Monoid[B]): F2[B] = {
...}
/**
* Collect the outputs of this `Process[F,O]`, given a `Monad[F]` in
* which we can catch exceptions. This function is not tail recursive and
* relies on the `Monad[F]` to ensure stack safety.
*/
final def runLog[F2[x] >: F[x], O2 >: O](implicit F: Monad[F2], C: Catchable[F2]): F2[Vector[O2]] = {
...}
/** Run this `Process` solely for its final emitted value, if one exists. */
final def runLast[F2[x] >: F[x], O2 >: O](implicit F: Monad[F2], C: Catchable[F2]): F2[Option[O2]] = {
...}
/** Run this `Process` solely for its final emitted value, if one exists, using `o2` otherwise. */
final def runLastOr[F2[x] >: F[x], O2 >: O](o2: => O2)(implicit F: Monad[F2], C: Catchable[F2]): F2[O2] =
runLast[F2, O2] map { _ getOrElse o2 }
/** Run this `Process`, purely for its effects. */
final def run[F2[x] >: F[x]](implicit F: Monad[F2], C: Catchable[F2]): F2[Unit] =
F.void(drain.runLog(F, C))
这几个函数都返回F2运算,如果F2是Task的话那么我们就可以用Task.run来获取结果值:
//runFoldMap就好比Monoid的sum
p.runFoldMap(identity).run //> res6: Int = 6
p.runFoldMap(i => i * ).run //> res7: Int = 12
p.runFoldMap(_.toString).run //> res8: String = 123
//runLog把收到的元素放入vector中
p.runLog.run //> res9: Vector[Int] = Vector(1, 2, 3)
//runLast取最后一个元素,返回Option
p.runLast.run //> res10: Option[Int] = Some(3)
Process.halt.toSource.runLast.run //> res11: Option[Nothing] = None
Process.halt.toSource.runLastOr().run //> res12: Int = 65
//run只进行F的运算,放弃所有元素
p.run //> res13: scalaz.concurrent.Task[Unit] = scalaz.concurrent.Task@26b3fd41
p.run.run //Task[Unit] 返回Unit
Process.emit(print("haha")).toSource.run.run //> haha
与List和Stream操作相似,我们同样可以对scalar-stream Process施用同样的操作函数,也就是一些stream转换函数:
p.take().runLog.run //> res14: Vector[Int] = Vector(1, 2)
p.filter {_ > }.runLog.run //> res15: Vector[Int] = Vector(3)
p.last.runLog.run //> res16: Vector[Int] = Vector(3)
p.drop().runLog.run //> res17: Vector[Int] = Vector(2, 3)
p.exists{_ > }.runLog.run //> res18: Vector[Boolean] = Vector(false)
以上这些函数与scala标准库的stream很相似。再看看map,flatMap吧:
p.map{i => s"Int:$i"}.runLog.run //> res19: Vector[String] = Vector(Int:1, Int:2, Int:3)
p.flatMap{i => Process(i,i-)}.runLog.run //> res20: Vector[Int] = Vector(1, 0, 2, 1, 3, 2)
仔细检查可以看出来上面的这些转换操作都是针对Process1类型的,都是元素在流通过程中得到转换。我们会在下篇讨论中介绍一些更复杂的Process操作,如:Sink,Tee,Wyn...,然后是scalaz-stream的具体应用
Scalaz(46)- scalaz-stream 基础介绍的更多相关文章
- Node.js学习笔记(一)基础介绍
什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...
- Node.js 基础介绍
什么是Node.js 官网介绍: Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...
- OSPF基础介绍
OSPF基础介绍 一.RIP的缺陷 1.以跳数评估的路由并非最优路径 2.最大跳数16导致网络尺度小 3.收敛速度慢 4.更新发送全部路由表浪费网络资源 二.OSPF基本原理 1.什么是OSPF a& ...
- Erlang基础 -- 介绍 -- Wordcount示例演示
在前两个blog中,已经说了Erlang的历史.应用场景.特点,这次主要演示一个Wordcount的示例,就是给定一个文本文件,统计这个文本文件中的单词以及该单词出现的次数. 今天和群友们讨论了一个问 ...
- Web服务基础介绍
Web服务基础介绍 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.正常情况下的单次web服务访问流程 博主推荐阅读: https://www.cnblogs.com/yinzh ...
- XML基础介绍【一】
XML基础介绍[一] 1.XML简介(Extensible Markup Language)[可扩展标记语言] XML全称为Extensible Markup Language, 意思是可扩展的标记语 ...
- Web3D编程入门总结——WebGL与Three.js基础介绍
/*在这里对这段时间学习的3D编程知识做个总结,以备再次出发.计划分成“webgl与three.js基础介绍”.“面向对象的基础3D场景框架编写”.“模型导入与简单3D游戏编写”三个部分,其他零散知识 ...
- C++ 迭代器 基础介绍
C++ 迭代器 基础介绍 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围.迭代器就如同一个指针.事实上,C++的指针也是一种迭代器.但是,迭代器不仅仅是指针,因此你不能认为他们一定 ...
- 1、git基础介绍及远程/本地仓库、分支
1. Git基础介绍 基于Git进行开发时,首先需要将远程仓库代码clone到本地,即为本地仓库.后续大部分时间都是基于本地仓库上的分支进行编码,最后将本地仓库的代码合入远程仓库. 1.1. 远程仓库 ...
- git基础介绍
git基础介绍 这是git操作的基础篇,是以前的写的操作文档,就没有进行手打,直接把图片贴进来了,你们担待哈,有不正确的地方可以指正出来,我将在第一时间去修改,多谢哈! 一.文件状态:git系统的文件 ...
随机推荐
- 《鸟哥的linux私房菜》 - linux命令温故而知新
在公司的某角落里,看到了<鸟哥的linux私房菜>,顿时想看看是什么鬼. 其他时候还要自己去买才有,现在正好,比图书馆方便.看完了,写点啥! 编辑器很重要,一个vim就主要是我的使用方向: ...
- spring之BeanFactory
spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后 ...
- Javascript快速入门(上篇)
Javascript的熟练之路,小弟来了. JavaScript简介:JavaScript一种直译式脚本语言,是一种动态类型.弱类型.基于原型的语言,内置支持类型.它的解释器被称为JavaScript ...
- Index Seek和Index Scan的区别
Index Seek是Sql Server执行查询语句时利用建立的索引进行查找,索引是B树结构,Sql Server先查找索引树的根节点,一级一级向下查找,在查找到相应叶子节点后,取出叶子节点的数据. ...
- 使用Connetion的属性RetainSameConnection
在SSIS的组件中,很多都会连接到数据库进行操作,Connection有一个属性RetainSameConnection,默认值是False,控制着连接的打开和关闭的时机. 1,如果Connectio ...
- ::after::before清除浮动原理
先来看一段代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
- 使用SQL Server Audit记录数据库变更
最近工作中有一个需求,就是某一个比较重要的业务表经常被莫名其妙的变更.在SQL Server中这类工作如果不事前捕获记录的话,无法做到.对于捕获变更来说,可以考虑的选择包括Trace,CDC. ...
- 前端优化-Img与background
当img 遇到 background,这个世界就是这么小~~~~~ 1 从解析机制看 Img属于html标签,background是css方法.一个页面由html.css.js组成,按照浏览器解析机制 ...
- Kruskal算法(三)之 Java详解
前面分别通过C和C++实现了克鲁斯卡尔,本文介绍克鲁斯卡尔的Java实现. 目录 1. 最小生成树 2. 克鲁斯卡尔算法介绍 3. 克鲁斯卡尔算法图解 4. 克鲁斯卡尔算法分析 5. 克鲁斯卡尔算法的 ...
- 可视化工具solo show-----Processing Prefuse show
继上篇<可视化工具solo show>罗列出一些主要基于Java开发的软件.插件之后,又仔细揣摩了下哪些可以为我所用. 一番端详之后,准备挑出其中Processing和Prefuse两位大 ...