FunDA(3)- 流动数据行操作:FDAPipeLine operations using scalaz-stream-fs2
在上节讨论里我们介绍了数据行流式操作的设想,主要目的是把后台数据库的数据载入前端内存再拆分为强类型的数据行,这样我们可以对每行数据进行使用和处理。形象点描述就是对内存里的一个数据流(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的更多相关文章
- MySQL之唯一索引、外键的变种、SQL语句数据行操作补充
0.唯一索引 unique对num进行唯一限制,表示num是独一无二的,uql是唯一索引名称 上面为联合索引:num和xx不能完全一样 1.外键的变种 a. 用户表和部门表 用户: 1 alex 1 ...
- 传智播客JavaWeb day09-mysql入门、数据库操作、数据库表操作、数据行操作
不知不觉已到了第九天了,今天主要讲了关系数据库的基本概述.安装.数据库.表和数据行的操作 1. 基本概述 1.1 数据库就是用来存储数据的.早期是存在文件里面的操作起来效率低而且不是很安全. 1.2 ...
- Oracle多用户对一个表进行并发插入数据行操作
oracle数据库支持多用户间同时对同一个表进行操作,但是数据不一定同步,因为oracle数据库是支持脏数据的,比如A用户删除了表的数据但没有提交,B用户也能查询访问到,如果要避免这种情况只能加锁,A ...
- 设置mysql数据表列自动递增以及数据行插入操作
创建mysql数据表,设置id列递增.主键create table running_log ( id int primary key auto_increment, routename varchar ...
- ASP.NET Aries 入门开发教程7:DataGrid的行操作(主键操作区)
前言: 抓紧勤奋,再接再励,预计共10篇来结束这个系列. 上一篇介绍:ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑 本篇介绍主键操作区相关内容. 1:什么时候有默认的 ...
- ASP.NET MVC搭建项目后台UI框架—8、将View中选择的数据行中的部分数据传入到Controller中
目录 ASP.NET MVC搭建项目后台UI框架—1.后台主框架 ASP.NET MVC搭建项目后台UI框架—2.菜单特效 ASP.NET MVC搭建项目后台UI框架—3.面板折叠和展开 ASP.NE ...
- JAVASE02-Unit08: 文本数据IO操作 、 异常处理
Unit08: 文本数据IO操作 . 异常处理 * java.io.ObjectOutputStream * 对象输出流,作用是进行对象序列化 package day08; import java.i ...
- MySQL的数据库,数据表,数据的操作
数据库简介 概念 什么是数据库?简单来说,数据库就是存储数据的"仓库", 但是,光有数据还不行,还要管理数据的工具,我们称之为数据库管理系统! 数据库系统 = 数据库管理系统 + ...
- mysql数据表操作&库操作
首先登陆mysql:mysql -uroot -proot -P3306 -h127.0.0.1 查看所有的库:show databases; 进入一个库:use database; 显示所在的库:s ...
随机推荐
- HDU 2079-课程时间(生成函数)
课程时间(标题已被修改,注意阅读题) Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- C# TextBox实现全选
A. 设置全局变量: 1.定义了个全局变量放本次点击的textbox的名字,默认为空. 2.textbox的Enter事件里SelectAll()一下 3.Click事件里判断全局变量是否是该text ...
- Mac OSX系统安装和配置Zend Server 6教程(2)
继上一节安装好Zend Server 6以后,我们需要修改配置文件.首先修改服务器监听端口.默认的情况下Zend Server 6安装以后的端口是10088.一般开发者使用的都是HTTP默认端口80. ...
- Scala 的 Web 框架 Lift 开始 3.0 版本开发
Scala 的 Web 框架 Lift 开始 3.0 版本开发 http://demo.liftweb.net/ http://liftweb.net/download Lift 框架在不断的成长和改 ...
- 读书笔记—CLR via C#线程25-26章节
前言 这本书这几年零零散散读过两三遍了,作为经典书籍,应该重复读反复读,既然我现在开始写博了,我也准备把以前觉得经典的好书重读细读一遍,并且将笔记整理到博客中,好记性不如烂笔头,同时也在写的过程中也可 ...
- Mysql高级之权限检查原理
原文:Mysql高级之权限检查原理 用户进行数据库操作分为两步: 1 是否有权限连接,根据host,name,password: 2 是否有权限进行CURD: 图示解说: 关于用户权限在哪里进行存放? ...
- Re-installation failed due to different application signatures.的解决方案
有时在安装不同版本apk文件时会出现Re-installation failed due to different application signatures.这样的提示 主要原因是安装的apk程序 ...
- Enterprise Architect UML 建模之活动图
EA(Enterprise Architect) UML 建模之活动图 一.活动图的概念作用 活动图本质上是一种流程图,它描述活动的序列,即系统从一个活动到另一个活动的控制流. 活动图的作用:描述 ...
- sql之T-SQL
sql之T-SQL 下面就T-SQL的几个方面来分别讲解一下. 1.变量 要动态的写sql语句,就不能没有变量. 声明变量并赋值: 1 declare @i as int;--定义一个 int 类 ...
- 【ios开发】Block编程
1 什么是block iOS SDK 4.0开始,Apple引入了block这一特性.字面上说,block就是一个代码块,但是它的神奇之处在于在内联(inline)执行的时候(这和C++很像)还可以传 ...