List是一种最普通的泛函数据结构,比较直观,有良好的示范基础。List就像一个管子,里面可以装载一长条任何类型的东西。如需要对管子里的东西进行处理,则必须在管子内按直线顺序一个一个的来,这符合泛函编程的风格。与其它的泛函数据结构设计思路一样,设计List时先考虑List的两种状态:空或不为空两种类型。这两种类型可以用case class 来表现:

     trait List[+A] {}
case class Cons[+A](head: A, tail: List[A]) extends List[A]
case object Nil extends List[Nothing]

以上是一个可以装载A类型元素的List,是一个多态的类型(Polymorphic Type)。+A表示List是协变(Covariant)的,意思是如果apple是fruit的子类(subtype)那么List[apple]就是List[fruit]的子类。Nil继承了List[Nothing],Nothing是所有类型的子类。结合协变性质,Nil可以被视为List[Int],List[String]...

List的另一种实现方式:

     trait List[+A] {
def node: Option[(A, List[A])]
def isEmpty = node.isEmpty
}
object List {
def empty[A] = new List[A] { def node = None}
def cons[A](head: A, tail: List[A]) = new List[A] { def node = Some((head, tail))}
}

以上代码中empty,cons两个方法可以实现List的两个状态。

我们还是采用第一种实现方式来进行下面有关List数据运算的示范。第二种方式留待Stream的具体实现示范说明。

先来个List自由构建器:可以用List(1,2,3)这种形式构建List:

     object List {
def apply[A](as: A*): List[A] = {
if (as.isEmpty) Nil
else Cons(as.head,apply(as.tail:_*))
}
}

说明:使用了递归算法来处理可变数量的输入参数。apply的传入参数as是个数组Array[A],我们使用了Scala标准集合库Array的方法:as.head, as.tail。示范如下:

 scala> Array(1,2,3).head
res11: Int = 1 scala> Array(1,2,3).tail
res12: Array[Int] = Array(2, 3)

增加了apply方法后示范一下List的构成:

 val li = List(1,2,3)                              //> li  : ch3.list.List[Int] = Cons(1,Cons(2,Cons(3,Nil)))
val ls = List("one","two","three") //> ls : ch3.list.List[String] = Cons(one,Cons(two,Cons(three,Nil)))

与以下方式对比,写法简洁多了:

 val lInt = Cons(1,Cons(2,Cons(3,Nil)))            //> lInt  : ch3.list.Cons[Int] = Cons(1,Cons(2,Cons(3,Nil)))

再来试一个运算:计算List[Int]里所有元素的和,还是用模式匹配和递归方式来写:

     trait List[+A] {
def sum: Int = this match {
case Nil => 0
case Cons(h: Int,t: List[Int]) => h + t.sum
}
}

我们把sum的实现放到特质申明里就可以用以下简洁的表达方式了:

 List(1,2,3) sum                                   //> res0: Int = 6

再试着玩多态函数sum:

       def sum[B >: A](z: B)(f: (B,B) => B): B = this match {
case Nil => z
case Cons(h,t) => f(h, t.sum(z)(f))
}

现在可以分别试试List[Int]和List[String]:

 List(1,2,3).sum(0){_ + _}                         //> res0: Int = 6
List("hello",",","World","!").sum(""){_ + _} //> res1: String = hello,World!

以下是一些List常用的函数:

     trait List[+A] {

       def head: A = this match {
case Nil => sys.error("Empty List!")
case Cons(h,t) => h
}
def tail: List[A] = this match {
case Nil => sys.error("Empty List!")
case Cons(h,t) => t
}
def take(n: Int): List[A] = n match {
case k if(k<0) => sys.error("index < 0 !")
case 0 => Nil
case _ => this match {
case Nil => Nil
case Cons(h,t) => Cons(h,t.take(n-1))
}
}
def takeWhile(f: A => Boolean): List[A] = this match {
case Nil => Nil
case Cons(h,t) => if(f(h)) Cons(h,t.takeWhile(f)) else Nil
}
def drop(n: Int): List[A] = n match {
case k if(k<0) => sys.error("index < 0 !")
case 0 => this
case _ => this match {
case Nil => Nil
case Cons(h,t) => t.drop(n-1)
}
}
def dropWhile(f: A => Boolean): List[A] = this match {
case Nil => Nil
case Cons(h,t) => if (f(h)) t.dropWhile(f) else this
}
}

看看以上的这些函数;是不是都比较相似?那是因为都是泛函编程风格的原因。主要以模式匹配和递归算法来实现。以下是使用示范:

 List(1,2,3).head                                  //> res0: Int = 1
List(1,2,3).tail //> res1: ch3.list.List[Int] = Cons(2,Cons(3,Nil))
List(1,2,3).take(2) //> res2: ch3.list.List[Int] = Cons(1,Cons(2,Nil))
List(1,2,3).takeWhile(x => x < 3) //> res3: ch3.list.List[Int] = Cons(1,Cons(2,Nil))
List(1,2,3) takeWhile {_ < 3} //> res4: ch3.list.List[Int] = Cons(1,Cons(2,Nil))
List(1,2,3).drop(2) //> res5: ch3.list.List[Int] = Cons(3,Nil)
List(1,2,3).dropWhile(x => x < 3) //> res6: ch3.list.List[Int] = Cons(3,Nil)
List(1,2,3) dropWhile {_ < 3} //> res7: ch3.list.List[Int] = Cons(3,Nil)

试试把一个List拼在另一个List后面:

         def ++[B >: A](a: List[B]): List[B] = this match {
case Nil => a
case Cons(h,t) => Cons(h,t.++(a))
}
 ist(1,2) ++ List(3,4)                            //> res8: ch3.list.List[Int] = Cons(1,Cons(2,Cons(3,Cons(4,Nil))))

只是想试试Scala的简洁表达方式。

噢,漏了两个:

       def init: List[A] = this match {
case Cons(_,Nil) => Nil
case Cons(h,t) => Cons(h,t.init)
}
def length: Int = this match {
case Nil => 0
case Cons(h,t) => 1 + t.length
}
 List(1,2,3).init                                  //> res9: ch3.list.List[Int] = Cons(1,Cons(2,Nil))
List(1,2,3).length //> res10: Int = 3

下面把几个泛函数据结构通用的函数实现一下:

       def map[B](f: A => B): List[B] = this match {
case Nil => Nil
case Cons(h,t) => Cons(f(h),( t map f))
}
def flatMap[B]( f: A => List[B]): List[B] = this match {
case Nil => Nil
case Cons(h,t) => f(h) ++ ( t flatMap f )
}
def filter(f: A => Boolean): List[A] = this match {
case Nil => Nil
case Cons(h,t) => if (f(h)) Cons(h,t.filter(f)) else t.filter(f)
}
 List(1,2,3) map {_ + 10}                          //> res13: ch3.list.List[Int] = Cons(11,Cons(12,Cons(13,Nil)))
List(1,2,3) flatMap {x => List(x+10)} //> res14: ch3.list.List[Int] = Cons(11,Cons(12,Cons(13,Nil)))
List(1,2,3) filter {_ != 2} //> res15: ch3.list.List[Int] = Cons(1,Cons(3,Nil))

这几个函数有多种实现方法,使Scala for-comprehension对支持的数据结构得以实现。有关这几个函数在泛函编程里的原理和意义在后面的有关Functor,Applicative,Monad课题里细说。

泛函编程(6)-数据结构-List基础的更多相关文章

  1. 泛函编程(5)-数据结构(Functional Data Structures)

    编程即是编制对数据进行运算的过程.特殊的运算必须用特定的数据结构来支持有效运算.如果没有数据结构的支持,我们就只能为每条数据申明一个内存地址了,然后使用这些地址来操作这些数据,也就是我们熟悉的申明变量 ...

  2. 泛函编程(8)-数据结构-Tree

    上节介绍了泛函数据结构List及相关的泛函编程函数设计使用,还附带了少许多态类型(Polymorphic Type)及变形(Type Variance)的介绍.有关Polymorphism的详细介绍会 ...

  3. 泛函编程(7)-数据结构-List-折叠算法

    折叠算法是List的典型算法.通过折叠算法可以实现众多函数组合(function composition).所以折叠算法也是泛函编程里的基本组件(function combinator).了解折叠算法 ...

  4. 泛函编程(14)-try to map them all

    虽然明白泛函编程风格中最重要的就是对一个管子里的元素进行操作.这个管子就是这么一个东西:F[A],我们说F是一个针对元素A的高阶类型,其实F就是一个装载A类型元素的管子,A类型是相对低阶,或者说是基础 ...

  5. 泛函编程(34)-泛函变量:处理状态转变-ST Monad

    泛函编程的核心模式就是函数组合(compositionality).实现函数组合的必要条件之一就是参与组合的各方程序都必须是纯代码的(pure code).所谓纯代码就是程序中的所有表达式都必须是Re ...

  6. 泛函编程(30)-泛函IO:Free Monad-Monad生产线

    在上节我们介绍了Trampoline.它主要是为了解决堆栈溢出(StackOverflow)错误而设计的.Trampoline类型是一种数据结构,它的设计思路是以heap换stack:对应传统递归算法 ...

  7. 泛函编程(29)-泛函实用结构:Trampoline-不再怕StackOverflow

    泛函编程方式其中一个特点就是普遍地使用递归算法,而且有些地方还无法避免使用递归算法.比如说flatMap就是一种推进式的递归算法,没了它就无法使用for-comprehension,那么泛函编程也就无 ...

  8. 泛函编程(28)-粗俗浅解:Functor, Applicative, Monad

    经过了一段时间的泛函编程讨论,始终没能实实在在的明确到底泛函编程有什么区别和特点:我是指在现实编程的情况下所谓的泛函编程到底如何特别.我们已经习惯了传统的行令式编程(imperative progra ...

  9. 泛函编程(27)-泛函编程模式-Monad Transformer

    经过了一段时间的学习,我们了解了一系列泛函数据类型.我们知道,在所有编程语言中,数据类型是支持软件编程的基础.同样,泛函数据类型Foldable,Monoid,Functor,Applicative, ...

随机推荐

  1. C#中yield return用法分析

    这篇文章主要介绍了C#中yield return用法,对比使用yield return与不使用yield return的流程,更直观的分析了yield return的用法,需要的朋友可以参考下. 本文 ...

  2. 在UWP应用中实现Gif播放

    众所周知,在UWP应用框架中,Image控件是无法播放GIF的图片,只能显示静态图,这样的体验不是特别友好.我在Win8.WP8.1的时候实现过gif播放功能,但是最近发现性能和播放效果都差强人意,大 ...

  3. Selenium Grid 学习笔记

    Selenium Grid 学习笔记http://www.docin.com/p-765680298.html

  4. asp.net mvc 配合前端js的CMD模块化部署思想,小思路

    1)布局视图,用于协调普通视图和共享视图,实现js库自由选择引用 @{ //具体页面定义好需要引入的js文件,在这里通过共享视图渲染出来 var jsDefines = ViewBag.JsDefin ...

  5. 【Android】Handler、Looper源码分析

    一.前言 源码分析使用的版本是 4.4.2_r1. Handler和Looper的入门知识以及讲解可以参考我的另外一篇博客:Android Handler机制 简单而言:Handler和Looper是 ...

  6. 【原】Oracle11gR2图文安装

    作者:david_zhang@sh [转载时请以超链接形式标明文章] http://www.cnblogs.com/david-zhang-index/p/4365475.html 本文以看图说话的方 ...

  7. SNF开发平台WinForm之六-上传下载组件使用-SNF快速开发平台3.3-Spring.Net.Framework

    6.1运行效果: 6.2开发实现: 1.先在要使用的项目进行引用,SNF.WinForm.Attachments.dll文件. 2.在工具箱内新建选项卡->选择项,浏览找到文件SNF.WinFo ...

  8. mysql 行转列

    SELECTREPLACE(GROUP_CONCAT(IF(ItemID=1101,ItemValue,"")),',',"") AS 'Item1101',R ...

  9. ruby -- 基础学习(一)项目文件夹说明

    App文件夹子文件夾功能介绍 (1)controllers  存放驱动业务逻辑的控制器 (2)helpers       存放视图辅助类,一些常用的代码段 (3)mailers       Rails ...

  10. Java定位CPU使用高问题--转载

    原文地址:http://www.cnblogs.com/guoyuqiangf8/p/3545687.html 1.TOP命令,查询消耗CPU高的进程号 PID,并记录下来,按下键盘"H&q ...