zoukankan      html  css  js  c++  java
  • 第二章 函数式数据结构

    一. 定义一个函数式的List

    1. 通常使用trait关键字引入一种数据类型
    2. sealed trait表示这个trait的所有实现都必须定义在这个文件里
    3. MyList有2种实现,空和非空。非空借口由初始的head元素和接下来的MyList结构的tail组成。Cons由constructer缩写
    4. MyList[A+]:类型正向型变,若Dog是Animal的子类,则List[Dog]是List[Animal]的子类
    5. Nil继承了MyList[Nothing],而Nothing是所有类型的子类型,所以List[Nothing]可以当成List[Int],List[Double]。
    6. 伴生对象是一个单例对象
    7. apply方法,使得MyList声名出来后,每个结构都是head和tail的MyList类型
      sealed trait MyList[+A]     // 泛型类型
      case object Nil extends MyList[Nothing]   // 空List类型
      case class Cons[+A](head:A,tail:MyList[A]) extends MyList[A]    //非空list,开头是一个点,尾部是另一个List
      
      object MyList{
        def sum(ints:MyList[Int]):Int = ints match { // 利用模式匹配,对整数型list元素进行求和
          case Nil => 0    // 控列表的累加值为0
          case Cons(x,xs) => x + sum(xs)
        }
      
        def product(ds:MyList[Double]):Double = ds match { // 对list的值进行求积
          case Nil => 1.0
          case Cons(0.0,_) => 0.0
          case Cons(x,xs) => x * product(xs)
        }
      
        def apply[A](as:A*):MyList[A] = // A* 是可变参数列表,多个类型为A的参数
          if(as.isEmpty)
            Nil
          else
            Cons(as.head,apply(as.tail:_*))
      }
      

    二. 函数式数据结构中的数据共享

    1. 函数式数据结构是持久的,即已存在的引用不会因数据结构的操作而改变。即函数式编程下产生的数据结构是不可变的

    2. 函数式数据结构不可变,意味着对原先的数据结构增删一个元素会返回一个全新的结果,这个结果不用去复制一份数据,可以直接复用他。

    3. 函数式数据结构的操作是以模式匹配+递归构成的

      /**
        * Created by lj on 17-2-22.
        */
      
      object MyList{
        // 获取tail部分
        def tail[A](l:MyList[A]):MyList[A] = {
          l match {
            case Nil => sys.error("tail of empty list")
            case Cons(_,t) => t
          }
        }
      
        //修改第一个元素的值
        def setHead[A](l:MyList[A],h:A):MyList[A] = {
          l match {
            case Nil => sys.error("head of empty list")
            case Cons(_,t) => Cons(h,t)
          }
        }
      
        // 删除前n个元素
        def drop[A](l:MyList[A],n:Int):MyList[A] = {
          if (n<=0)
            l
          else l match {
            case Nil => Nil
            case Cons(_,t) => drop(t,n-1)
          }
        }
      
        // 删除前面符合条件的元素
        def dropWhile[A](l:MyList[A],f:A=>Boolean) :MyList[A] = {
          l match {
            case Nil => Nil
            case Cons(h,t) if f(h) => dropWhile(t,f)
          }
        }
      
        // 将一个列表的元素加到另一个元素的后面
        /** 该方法只做数据复制,直到第一个列表中没有元素可用。所以时间和内存开销只取决于l1的长度
          * 若我们用两个数组实现相同的函数,则被迫要复制两个数组中的所有元素到一个数组中
          */
        def append[A] (l1:MyList[A],l2:MyList[A]) :MyList[A] ={
          l1 match {
            case Nil => l2
            case Cons(h,t) => Cons(h,append(t,l2))
            //case Cons(h,t) => append(t,Cons(h,l2))
          }
        }
      
        // 返回除最后一个元素外的所有元素
        // 这个函数不能实现像tail一样的常亮级时间
        def init[A](l:MyList[A]):MyList[A] = {
          l match {
            case Nil => sys.error("init of empty")
            case Cons(_,Nil) => Nil
            case Cons(h,t) => Cons(h,init(t))
          }
        }
      
        // 用高阶函数重写sum和product(这两个函数的代码大量重复)
        def folderight[A,B](l:MyList[A],z:B)(f:(A,B)=>B):B = {
          l match {
            case Nil => z
            case Cons(x,xs) => f(x,folderight(xs,z)(f))
          }
        }
      
        def sum2(l:MyList[Int]) = folderight(l,0)((x,y)=>x+y)
        def product2(l:MyList[Double]) = folderight(l,1.0)(_ * _)
        def length[A](l:MyList[A]) : Int ={
          folderight(l,0)((_,acc) => acc+1)
        }
      
        // 用folderleft改进folderright,形成尾递归版的sum,product
        def foldleft[A,B](l:MyList[A],z:B)(f:(B,A)=>B):B = {
          l match{
            case Nil => z
            case Cons(h,t) => foldleft(t,f(z,h))(f)
          }
        }
        def sum3(l:MyList[Int]) = foldleft(l,0)(_+_)
        def product3(l:MyList[Double]) = foldleft(l,1.0)(_*_)
        def length2[A](l:MyList[A]) = foldleft(l,0)((a,b)=>a+1)
        def reverse[A](l:MyList[A]) = foldleft(l,MyList[A]())((acc,h) => Cons(h,acc))
      }
      
  • 相关阅读:
    python 网络爬虫框架scrapy使用说明
    计算机数据表示
    NoSQL 数据库应用
    什么是java序列化?什么情况下需要序列化?
    怎么实现动态代理?
    动态代理是什么?应用场景?
    什么是反射?有什么作用?
    nio中的Files类常用方法有哪些?
    什么是JAVA内部类?
    常见的异常类有哪些?
  • 原文地址:https://www.cnblogs.com/moonlord/p/6438561.html
Copyright © 2011-2022 走看看