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

    [题意简述]:题意就是输入一串字符串,问我们有多少种不同的字符,也就是说出现过一次的字符,下次就不记到种数中了,特别的有  ff, fi ,fl ,ffi ,ffl,'',``, 这几个每一个算是一种 ...

  2. 邮箱自动完成(jquary效果)

    邮箱自动完成的效果在网站上大多都看过,但是质量参差不齐,今天突然在网上看到一篇博客,感觉这个插件很好,就想来写一下分享给大家! 效果图如下! 完整demo代码如下: <!DOCTYPE html ...

  3. iScroll 4.2.5 中文API

    概况 资料来源 http://cubiq.org/iscroll-4 http://www.cnblogs.com/wanghun/archive/2012/10/17/2727416.html ht ...

  4. Android项目---LayoutParams

    LinearLayout.LayoutParams extends ViewGroup.MarginLayoutParams java.lang.Object    ↳ android.view.Vi ...

  5. 枚举for/in

    for/in循环可以遍历对象中所有可以枚举的属性(包括自有属性和继承属性).对象继承的内置方法不能枚举,凡是在代码中给对象自己或者继承的类添加的属性方法都是可枚举的,但是对象自有的内置属性可不可以枚举 ...

  6. php中session和cookie

    cookie 每次请求页面的时候进行验证,如果用户信息存储在数据库中,每次都要执行一次数据库查询,给数据库造成多余的负担.cookie可以被修改的,所以安全系数太低. session是存储在服务器端面 ...

  7. 在现有代码中通过async/await实现并行

    在现有代码中通过async/await实现并行 一项新技术或者一个新特性,只有你用它解决实际问题后,才能真正体会到它的魅力,真正理解它.也期待大家能够多分享解一些解决实际问题的内容. 在我们遭遇“黑色 ...

  8. KMP算法详解-- 转自Matrix67

       6 7 8 9 --    A = a b a b  6 7  7 8 9 --    A = a b a b a  5 6 7  8 9 --    A = a b a b a b  6 7 ...

  9. [Usaco2008 Dec]Patting Heads 轻拍牛头[筛法]

    Description   今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.     贝茜让N(1≤N≤100000)头奶牛坐成一个圈.除了1号与N号奶牛外,i号奶牛与i-l号和i+l号奶 ...

  10. 小菜的SharePoint Tips

    07. 设计模式应用案例(下) 前文用C#代码实现了Facade模式.Adapter模式.Strategy模式.Bridge模式和Abstract Factory模式解决实际业务需求.本文将继续以C# ...