Scalaz(46)- scalaz-stream 基础介绍

来源:互联网 发布:支持php免费空间 编辑:程序博客网 时间:2024/04/29 02:47

    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(1)                                  //> res0: scalaz.stream.Process0[Int] = Emit(Vector(1)) Process.emitAll(Seq(1,2,3))                      //> res1: scalaz.stream.Process0[Int] = Emit(List(1, 2, 3)) Process.halt                                     //> res2: scalaz.stream.Process0[Nothing] = Halt(End) Process.range(1,2,3)           //> res3: scalaz.stream.Process0[Int] = Append(Halt(End),Vector(<function1>))


这些是纯数据流的构建方法。scalaz-stream通常把Task作为F运算,下面是Task运算流的构建或者转换方法:

val p: Process[Task,Int] = Process.emitAll(Seq(1,2,3))  //> p  : scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>)) Process.range(1,2,3).toSource                   //> res4: scalaz.stream.Process[scalaz.concurrent.Task,Int] = Append(Halt(End),Vector(<function1>)) //把F[A]升格成Process[F,A]Process.eval(Task.delay {5 * 8})                 //> 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 * 2).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(65).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(2).runLog.run                             //> res14: Vector[Int] = Vector(1, 2) p.filter {_ > 2}.runLog.run                      //> res15: Vector[Int] = Vector(3) p.last.runLog.run                                //> res16: Vector[Int] = Vector(3) p.drop(1).runLog.run                             //> res17: Vector[Int] = Vector(2, 3) p.exists{_ > 5}.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-1)}.runLog.run        //> res20: Vector[Int] = Vector(1, 0, 2, 1, 3, 2)


仔细检查可以看出来上面的这些转换操作都是针对Process1类型的,都是元素在流通过程中得到转换。我们会在下篇讨论中介绍一些更复杂的Process操作,如:Sink,Tee,Wyn...,然后是scalaz-stream的具体应用











0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 眼睛里拉出白丝怎么办 脚扭了脚面肿了怎么办 伪音唱歌嗓子疼怎么办 小孩吊水手肿了怎么办 棉质地的裙子起褶子怎么办 孕妇吃了马苋菜怎么办 三非黑人抓住了怎么办 33岁了写字好丑怎么办 裤子熨焦了发亮怎么办 黑裤子熨亮了怎么办 。P手机没有钤声怎么办 处处被小人其欠负怎么办??? 衣服开了一个口怎么办 脸上长痘痘留下的坑怎么办 脸部被打得皮肤怎么办 打脸引起耳朵疼怎么办 被打了耳痛耳鸣怎么办 苹果6视频锁屏怎么办 抠耳朵抠疼了怎么办 图库的相片没了怎么办 遇到打假牌的人怎么办 部队保障卡丢了怎么办 廊坊武警学院取消现役学员怎么办 孩子去当兵联系不上怎么办 军训戴眼镜晒痕怎么办 想进部队体检没过怎么办 大腿跟小腿不直怎么办 腿被车门夹了怎么办 脚出汗穿凉鞋滑怎么办 玩游戏手出汗屏幕滑怎么办 新买的鞋子臭怎么办 当公民利益受到侵犯怎么办 唇钉里面长肉怎么办 宝宝舔了一口酒怎么办 头被玻璃门撞了怎么办 30多了还一事无成 未来怎么办 27岁失业了该怎么办 无业证明不给开怎么办 典型的缺乏运动的肥胖怎么办 30岁了不想结婚怎么办 专家解释欠30万怎么办