在上节讨论里我们介绍了数据行流式操作的设想,主要目的是把后台数据库的数据载入前端内存再拆分为强类型的数据行,这样我们可以对每行数据进行使用和处理。形象点描述就是对内存里的一个数据流(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. 在ubuntu下把php的网页调试功能打开

    我这儿的环境是 Ubuntu 14.04  + Lighttpd + PHP5.5 默认情况下php的网页调试功能是不打开的,当PHP解析到一个错误的语法时会直接输出为空白. 我在网上找一许多文章,说 ...

  2. 分享12款经典时尚的HTML5应用

    分享伟大,呵呵.今天给大家分享一下收集的12个HTML5小特效. 我整理一下源码,给大家打包一下,我博客园上传文件大小有限,传不了了. 需要的请留下邮箱就行了,觉得好的话,不要忘了点赞哦~ 1.CSS ...

  3. Twitter Bootstrap JavaScript插件

    Twitter Bootstrap JavaScript插件本文收集了10款非常不错的JavaScript Twitter bootstrap扩展插件,利用Boostrap开发者可以节省大量的时间修复 ...

  4. STL非变易算法 - STL算法

    欢迎访问我的新博客:http://www.milkcu.com/blog/ 原文地址:http://www.milkcu.com/blog/archives/1394600460.html 原创:ST ...

  5. 排序算法学习,python实现

    原创博文,转载请注明出处 利用周六周末的时间把几种基本的排序方法用python实现了一下,废话少说,直接上代码. 本文不注重基础知识的讲解,只做大致的描述,大家如果不清楚概念,自行查找资料. 直接插入 ...

  6. HTTP压缩算法SDCH

    程序设计中使用的那些共享方法或者技术 前段时间看了个paper是讲述谷歌浏览器使用的压缩方法SDCH,其实原理还是比较简单的. 看了论文后就想总结一下程序中使用的一些共享方法或者技术吧. 1.Goog ...

  7. Repeater 模板中查找子控件

    前言:对于Repeater控件,相信从事NETWeb开发的同仁们再熟悉不过了.因其呈现方式和Literal一样,并不在前端生成任何表单标签元素,所以属于比较轻量级的控件.不过青睐于Repeater的主 ...

  8. 关于GNU软件的版本号命名规则

    这里所说的版本号命名并非指“正式版”.“测试版”这种方式,而是在讨论版本编号的问题,例如Linux内核3.0以后的版本命名规则是3.A.B,A是内核的版本,B是安全补丁.那么对于一般的软件的版本号命名 ...

  9. [转]How WebKit Loads a Web Page

    ref:https://www.webkit.org/blog/1188/how-webkit-loads-a-web-page/ Before WebKit can render a web pag ...

  10. springmvc国际化 基于请求的国际化配置

    springmvc国际化 基于请求的国际化配置 基于请求的国际化配置是指,在当前请求内,国际化配置生效,否则自动以浏览器为主. 项目结构图: 说明:properties文件中为国际化资源文件.格式相关 ...