泛函编程(8)-数据结构-Tree
上节介绍了泛函数据结构List及相关的泛函编程函数设计使用,还附带了少许多态类型(Polymorphic Type)及变形(Type Variance)的介绍。有关Polymorphism的详细介绍会放在typeclass讨论中。为了更多了解泛函数据结构(Functional Data Structure),想在这个章节把另一个我们熟悉的数据结构-Tree做些简单介绍。
Tree的状态不是枝(Branch)就是叶(Leaf),这个很容易理解。那么就按照上节设计List那样设计Tree类型:
trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
类参数+A代表协变(covariant),这个在上节List中已经介绍过了。先创建一个Tree实例(Tree Instance):
val tree = Branch(Branch(Leaf(1),Leaf(2)),Branch(Branch(Leaf(10),Leaf(8)),Leaf(3)))
//> tree : ch3.tree.Branch[Int] = Branch(Branch(Leaf(1),Leaf(2)),Branch(Branch(
//| Leaf(10),Leaf(8)),Leaf(3)))
创建了一个Tree Instance tree,如下图:
数数有几个节点:
def size: Int = this match {
case Leaf(_) => 1
case Branch(l,r) => 1 + l.size + r.size
}
tree size //> res0: Int = 9
这是所有branch + leaf 总数。
分开计算branch 和 leaf 数量:
def countLeafs: Int = this match {
case Leaf(_) => 1
case Branch(l,r) => 0 + l.size + r.size
}
def countBranches: Int = this match {
case Leaf(_) => 0
case Branch(l,r) => 1 + l.size + r.size
}
tree.countLeafs //> res1: Int = 8
tree.countBranches //> res2: Int = 9
探探最深有多深:
def depth: Int = this match {
case Leaf(_) => 0
case Branch(l,r) => 1 + (l.depth max r.depth)
}
tree depth //> res1: Int = 3
找出最大值的Leaf:
def maxValue: Int = this match {
case Leaf(a: Int) => a
case Branch(l,r) => l.maxValue max r.maxValue
}
tree maxValue //> res2: Int = 10
可以从以上这些函数得出一下共性。把共性抽象出来用fold来实现:
def fold[B](f: A => B)(g: (B,B) => B): B = this match {
case Leaf(n) => f(n)
case Branch(l,r) => g(l.fold(f)(g), r.fold(f)(g))
}
函数fold分别收到两个方法f,g:f用来处理Leaf,g用来处理Branch。看看用fold来实现上面的函数:
def sizeByfold = fold(a => 1)(1 + _ + _)
def maxValueByfold(l: Tree[Int]) = l.fold(a => a)((x,y) => 0 + (x max y))
def depthByfold = fold(a => 0)((x,y) => 1 + (x max y))
tree sizeByfold //> res3: Int = 9 tree depthByfold //> res4: Int = 3 tree.maxValueByfold(tree) //> res5: Int = 10
可能这个 tree.maxValueByfold(tree) 有点怪,但如果把函数实现放到 object Tree里然后import Tree._就可以了。
下面把map和flatMap实现了:
def map[B](f: A => B): Tree[B] = this match {
case Leaf(a) => Leaf(f(a))
case Branch(l,r) => Branch(l.map(f),r.map(f))
}
def flatMap[B](f: A => Tree[B]): Tree[B] = this match {
case Leaf(a) => f(a)
case Branch(l,r) => Branch(l.flatMap(f), r.flatMap(f))
}
泛函编程(8)-数据结构-Tree的更多相关文章
- 泛函编程(5)-数据结构(Functional Data Structures)
编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...
- 泛函编程(6)-数据结构-List基础
List是一种最普通的泛函数据结构,比较直观,有良好的示范基础.List就像一个管子,里面可以装载一长条任何类型的东西.如需要对管子里的东西进行处理,则必须在管子内按直线顺序一个一个的来,这符合泛函编 ...
- 泛函编程(7)-数据结构-List-折叠算法
折叠算法是List的典型算法.通过折叠算法可以实现众多函数组合(function composition).所以折叠算法也是泛函编程里的基本组件(function combinator).了解折叠算法 ...
- 泛函编程(34)-泛函变量:处理状态转变-ST Monad
泛函编程的核心模式就是函数组合(compositionality).实现函数组合的必要条件之一就是参与组合的各方程序都必须是纯代码的(pure code).所谓纯代码就是程序中的所有表达式都必须是Re ...
- 泛函编程(30)-泛函IO:Free Monad-Monad生产线
在上节我们介绍了Trampoline.它主要是为了解决堆栈溢出(StackOverflow)错误而设计的.Trampoline类型是一种数据结构,它的设计思路是以heap换stack:对应传统递归算法 ...
- 泛函编程(29)-泛函实用结构:Trampoline-不再怕StackOverflow
泛函编程方式其中一个特点就是普遍地使用递归算法,而且有些地方还无法避免使用递归算法.比如说flatMap就是一种推进式的递归算法,没了它就无法使用for-comprehension,那么泛函编程也就无 ...
- 泛函编程(25)-泛函数据类型-Monad-Applicative
上两期我们讨论了Monad.我们说Monad是个最有概括性(抽象性)的泛函数据类型,它可以覆盖绝大多数数据类型.任何数据类型只要能实现flatMap+unit这组Monad最基本组件函数就可以变成Mo ...
- 怎样学习Scala泛函编程
确切来说应该是我打算怎么去学习Scala泛函编程.在网上找不到系统化完整的Scala泛函编程学习资料,只好把能找到的一些书籍.博客.演讲稿.论坛问答.技术说明等组织一下,希望能达到学习目的.关于Sca ...
- 泛函编程(14)-try to map them all
虽然明白泛函编程风格中最重要的就是对一个管子里的元素进行操作.这个管子就是这么一个东西:F[A],我们说F是一个针对元素A的高阶类型,其实F就是一个装载A类型元素的管子,A类型是相对低阶,或者说是基础 ...
随机推荐
- Jenkins FTP 上传
需要插件:FTP publisher plugin 进入 Jenkins / 系统管理 / 系统设置 找到 FTP repository hosts,新增一个,编辑好,保存 打开 Jenkins / ...
- LNAMP架构中后端Apache获取用户真实IP地址的2种方法(转)
一.Nginx反向代理配置: 1.虚拟主机配置 复制代码代码如下: location / { try_files $uri @apache;} location @apache {interna ...
- (笔记)Linux内核学习(十)之虚拟文件系统概念
虚拟文件系统 虚拟文件系统:内核子系统VFS,VFS是内核中文件系统的抽象层,为用户空间提供文件系统相关接口: 通过虚拟文件系统,程序可以利用标准Linux文件系统调用在不同的文件系统中进行交互和操作 ...
- SQL语句 - 嵌套查询
嵌套查询的意思是,一个查询语句(select-from-where)查询语句块可以嵌套在另外一个查询块的where子句中,称为嵌套查询.其中外层查询也称为父查询,主查询.内层查询也称子查询,从查询. ...
- MYSQL 的一些文件及说明
1.MySQL库目录下db.opt文件的作用 http://my.oschina.net/u/1462678/blog/232719 2.复制MySQL数据库A到另外一个MySQL数据库B(仅仅针对i ...
- linux标准daemon编写方式
daemon定义 运行在后台的程序,通常不需要与用户进行交互的. 任何父进程id是0的通常是kernel进程,作为系统启动的一部分,除了init是用户态的命令. 规则 第一件事情是调用umask设置文 ...
- mysql int(1) 与 tinyint(1) 有什么区别?
From: http://www.cnblogs.com/xiaochaohuashengmi/archive/2011/08/25/2153016.html mysql int(1) tinyint ...
- SNF开发平台WinForm之五-高级查询使用说明-SNF快速开发平台3.3-Spring.Net.Framework
5.1运行效果: 5.2开发实现: 1.按上面效果来说,先来看一下在程序当中如果调用.第一步在页面拖拽一个按钮为“高级查询”,事件上写下如下代码: 如果是单表查询的话,只需要传GridView就行,如 ...
- smartjs 0.2 OOP讲解 - Klass 类继承
SmartJS2.0加入OOP的功能.OOP包括klass与factory两个对象. Klass 类继承 与其他的类继承相比,smartjs使用了执行指针的概念(后面例子中会介绍),另外提供base基 ...
- 代码演示用 KnockoutJS 和 Web API 对一个表格(Gird)进行 CRUD 操作,在 MVC 5 下
实体类: using System; using System.Collections.Generic; public partial class EmployeeInfo { public int ...