在上节讨论里我们介绍了数据行流式操作的设想,主要目的是把后台数据库的数据载入前端内存再拆分为强类型的数据行,这样我们可以对每行数据进行使用和处理。形象点描述就是对内存里的一个数据流(data-stream)进行逐行操作。我们在上节用foreach模拟了一个流控来示范数据行的操作处理。在这节我们讨论一下用scalaz-stream-fs2作为数据流管理工具来实现FunDA的数据行流动管理功能。fs2的Stream是一种自然的拖动型(pull-model)数据流。而fs2的Pipe类型则像是管道的阀门(valve),我们可以在Pipe里截获流动中的数据行。我们看看下面的fs2 Stream例子:

   def log[ROW](prompt: String): Pipe[Task,ROW,ROW] =
_.evalMap {row => Task.delay {println(s"$prompt> $row"); row}}
//> log: [ROW](prompt: String)fs2.Pipe[fs2.Task,ROW,ROW]
Stream.range(,).through(log("")).run.unsafeRun//> > 1
//| > 2
//| > 3
//| > 4

函数log是个Pipe类型。我们看到Pipe类型可以截获Stream中的流动元素,在函数log里我们通过evalMap来立即运算了println把当前的元素内容显示出来。所以我们并没有用runLog来收集Stream的元素(runLog也只能在完成所有元素的收集后才能显示结果)。

按照FunDA设计要求:从后台数据库中读取数据、载入内存然后逐行进行处理,那么我们可以用这个Pipe类型来实现数据的逐行处理,包括控制数据流动以及任意插入一些自定义数据元素。下面我们就试试通过定义Pipe类型的不同功能来实现行数据处理:

   def stopOn3[ROW]: Pipe[Task,ROW,ROW] = in => {
def go: Handle[Task,ROW] => Pull[Task,ROW,Unit] = h => {
h.receive1Option {
case Some((r,h)) => if ( == r) Pull.done
else Pull.output1(r) >> go(h)
case None => Pull.done
}
}
in.pull(go)
} //> stopOn3: [ROW]=> fs2.Pipe[fs2.Task,ROW,ROW]
Stream(,,,,,)
.through(log("before"))
.through(stopOn3)
.through(log("after"))
.run
.unsafeRun //> before> 4
//| after> 4
//| before> 2
//| after> 2
//| before> 9
//| after> 9
//| before> 3

stopOn3是个自定义Pipe。它的功能是截取当前元素、检查当前元素值、如果遇到3则终止数据流。从运算结果看:当before> 3时数据流停止流动(停止向下游发送元素)。虽然成功地实现了它的目的,函数stopOn3的设计者必须对fs2有较深的了解。而对于FunDA的终端用户来说不要说需要掌握fs2的运算机制,就连那些复杂的fs2类型就已经不可接受了。我想了一下:如果我们提供一个像stopOn3这样的Pipe函数、由用户提供有关的功能函数作为传入参数,这样的方式应该有比较大的接收空间。我们先从类型开始:重新模拟一套简明的与fs2类型相对应的FunDA类型:

   //数据处理管道
type FDAPipeLine[ROW] = Stream[Task,ROW]
//数据作业节点
type FDAWorkNode[ROW] = Pipe[Task,ROW,ROW]
//数据管道开关阀门,从此处获得管道内数据
type FDAValve[ROW] = Handle[Task,ROW]
//管道连接器
type FDAPipeJoint[ROW] = Pull[Task,ROW,Unit]

下面是用这些类型向用户提供的帮助函数(helpers):

   //库提供:停止数据流动
def fda_haltFlow = Pull.done //> fda_haltFlow: => fs2.Pull[Nothing,Nothing,Nothing]
//库提供:向下游发送一个ROW
def fda_sendRow[ROW](row: ROW) = Pull.output1(row) //> fda_sendRow: [ROW](row: ROW)fs2.Pull[Nothing,ROW,Unit]
//库提供:处理当前数据。运行用户提供的功能wf
def fda_doWork[ROW](wf: ROW => FDAPipeJoint[ROW]): FDAWorkNode[ROW] = {
def go: FDAValve[ROW] => FDAPipeJoint[ROW] = h => {
h.receive1Option {
case Some((r,h)) => wf(r) >> go(h)
case None => fda_haltFlow
}
}
in => in.pull(go)
} //> fda_doWork: [ROW](wf: ROW => demo.ws.FDAPipe.FDAPipeJoint[ROW])demo.ws.FDAPipe.FDAWorkNode[ROW]

现在看来貌似一旦用户可以提供一个ROW => FDAPipeJoint[ROW]函数,就可以用fda_doWork函数来运算这个函数了。我们按上面例子的功能要求来设计一个这样的函数:

  //样板用户提供数据处理功能函数
def breakOn3[ROW]: ROW => FDAPipeJoint[ROW] = row => {
if ( == row ) fda_haltFlow
else fda_sendRow(row)
} //> breakOn3: [ROW]=> ROW => demo.ws.FDAPipe.FDAPipeJoint[ROW]
//测试运算
Stream(,,,,,)
.through(log("before"))
.through(fda_doWork(breakOn3))
.through(log("after"))
.run
.unsafeRun //> before> 4
//| after> 4
//| before> 2
//| after> 2
//| before> 9
//| after> 9
//| before> 3

成功实现功能。下面是这篇讨论中的示范代码:

 import fs2._
object FDAPipe {
def log[ROW](prompt: String): Pipe[Task,ROW,ROW] =
_.evalMap {row => Task.delay {println(s"$prompt> $row"); row}}
Stream.range(,).through(log("")).run.unsafeRun
def stopOn3[ROW]: Pipe[Task,ROW,ROW] = in => {
def go: Handle[Task,ROW] => Pull[Task,ROW,Unit] = h => {
h.receive1Option {
case Some((r,h)) => if ( == r) Pull.done
else Pull.output1(r) >> go(h)
case None => Pull.done
}
}
in.pull(go)
}
Stream(,,,,,)
.through(log("before"))
.through(stopOn3)
.through(log("after"))
.run
.unsafeRun
//数据处理管道
type FDAPipeLine[ROW] = Stream[Task,ROW]
//数据作业节点
type FDAWorkNode[ROW] = Pipe[Task,ROW,ROW]
//数据管道开关阀门,从此处获得管道内数据
type FDAValve[ROW] = Handle[Task,ROW]
//管道连接器
type FDAPipeJoint[ROW] = Pull[Task,ROW,Unit] //库提供:停止数据流动
def fda_haltFlow = Pull.done
//库提供:向下游发送一个ROW
def fda_sendRow[ROW](row: ROW) = Pull.output1(row)
//库提供:处理当前数据。运行用户提供的功能wf
def fda_doWork[ROW](wf: ROW => FDAPipeJoint[ROW]): FDAWorkNode[ROW] = {
def go: FDAValve[ROW] => FDAPipeJoint[ROW] = h => {
h.receive1Option {
case Some((r,h)) => wf(r) >> go(h)
case None => fda_haltFlow
}
}
in => in.pull(go)
}
//用户提供数据处理功能函数
def breakOn3[ROW]: ROW => FDAPipeJoint[ROW] = row => {
if ( == row ) fda_haltFlow
else fda_sendRow(row)
}
//测试运算
Stream(,,,,,)
.through(log("before"))
.through(fda_doWork(breakOn3))
.through(log("after"))
.run
.unsafeRun
}

FunDA(3)- 流动数据行操作:FDAPipeLine operations using scalaz-stream-fs2的更多相关文章

  1. MySQL之唯一索引、外键的变种、SQL语句数据行操作补充

    0.唯一索引 unique对num进行唯一限制,表示num是独一无二的,uql是唯一索引名称 上面为联合索引:num和xx不能完全一样  1.外键的变种 a. 用户表和部门表 用户: 1 alex 1 ...

  2. 传智播客JavaWeb day09-mysql入门、数据库操作、数据库表操作、数据行操作

    不知不觉已到了第九天了,今天主要讲了关系数据库的基本概述.安装.数据库.表和数据行的操作 1. 基本概述 1.1 数据库就是用来存储数据的.早期是存在文件里面的操作起来效率低而且不是很安全. 1.2 ...

  3. Oracle多用户对一个表进行并发插入数据行操作

    oracle数据库支持多用户间同时对同一个表进行操作,但是数据不一定同步,因为oracle数据库是支持脏数据的,比如A用户删除了表的数据但没有提交,B用户也能查询访问到,如果要避免这种情况只能加锁,A ...

  4. 设置mysql数据表列自动递增以及数据行插入操作

    创建mysql数据表,设置id列递增.主键create table running_log ( id int primary key auto_increment, routename varchar ...

  5. ASP.NET Aries 入门开发教程7:DataGrid的行操作(主键操作区)

    前言: 抓紧勤奋,再接再励,预计共10篇来结束这个系列. 上一篇介绍:ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑 本篇介绍主键操作区相关内容. 1:什么时候有默认的 ...

  6. ASP.NET MVC搭建项目后台UI框架—8、将View中选择的数据行中的部分数据传入到Controller中

    目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI框架—3.面板折叠和展开 ASP.NE ...

  7. JAVASE02-Unit08: 文本数据IO操作 、 异常处理

    Unit08: 文本数据IO操作 . 异常处理 * java.io.ObjectOutputStream * 对象输出流,作用是进行对象序列化 package day08; import java.i ...

  8. MySQL的数据库,数据表,数据的操作

    数据库简介 概念 什么是数据库?简单来说,数据库就是存储数据的"仓库", 但是,光有数据还不行,还要管理数据的工具,我们称之为数据库管理系统! 数据库系统 = 数据库管理系统 + ...

  9. mysql数据表操作&库操作

    首先登陆mysql:mysql -uroot -proot -P3306 -h127.0.0.1 查看所有的库:show databases; 进入一个库:use database; 显示所在的库:s ...

随机推荐

  1. .NET程序保护专家.NET Reactor发布4.7版本

    .NET Reactor是一款功能强大的代码保护以及许可授权管理系统. 关于代码混淆,针对.NET程序程序而言,.NET Reactor保护的程序目前还没有被破解过.这与.NET Reactor的保护 ...

  2. RPL协议介绍

    RPL是IPv6 Routing Protocol for Low-Power and Lossy Networks的简称. 低功耗及有损网络(LLN)是一类内部链接和路由器都受限的网络,该网络下的路 ...

  3. 无线连接手机进行Android测试

    当每天走到哪都要拿一根数据线进行项目测试的时候,总是有一些焦急和烦躁的,如果能够无线连接测试就在好不过了. 这样不再是什么难事了,只需要几步走: 在进行无线连接测试的过程中,你的手机必须root了,这 ...

  4. 青铜器RDM全面支持CMMI、GJB5000A L2~L5认证评估

    青铜器RDM全面实现对CMMI L4.GJB5000A L4的100%支持,并且已经成为众多组织CMMI.GJB5000A落地执行的有效手段,避免认证与执行2张皮,有利于体系的贯彻执行,以下是青铜器R ...

  5. 一个Shift的后门程序,可以让你可以进入你不知道密码的电脑

    1.前提 你可以在平时亲身接触状态电脑,哪怕是在电脑主人不在的时候(虽然主人不在,或者关机了,进入电脑是要密码的). 2.原理 利用电脑连续按5次Shift会触发粘滞键,它会运行c:\winows\s ...

  6. 网络tcp/ip资料

    1. Linux TCP/IP 协议栈分析,这是chinaunix.net论坛里的N人写的电子书,可以在这里下载PDF版本.http://blog.chinaunix.net/u2/85263/sho ...

  7. 推荐5个漂亮的网站html源码

    给大家推荐几个很漂亮的html网站源码模板,下面就简单列几个,更多的自己可以去看 1.大屏背景摄影工作室作品案例网页模板 [效果预览及下载] 2.粉色响应式IT科技服务器机房企业模板 [效果预览及下载 ...

  8. 大数据时代,我们为什么使用hadoop

    大数据时代,我们为什么使用hadoop 我们先来看看大数据时代, 什么叫大数据,“大”,说的并不仅是数据的“多”!不能用数据到了多少TB ,多少PB 来说. 对于大数据,可以用四个词来表示:大量,多样 ...

  9. QuickWebApi2:使用Lambda方式,完成对WebApi的开发和调用-文档的生成

    续 QuickWebApi:使用Lambda方式,完成对WebApi的开发和调用 上一篇完成了主要的功能,本次修订主要重构了对接口文档的生成规范,使之可读性更佳,甚至可以作为接口文档进行发布(当然,在 ...

  10. linux 之 popen函数

    描述 popen() 函数 用 创建管道 的 方式启动一个 进程, 并调用 shell. 因为 管道是被定义成单向的, 所以 type 参数 只能定义成 只读或者 只写, 不能是 两者同时, 结果流也 ...