美文网首页
函数式数据结构(1)

函数式数据结构(1)

作者: 吐思圈 | 来源:发表于2018-02-18 00:07 被阅读0次

    函数式数据结构只能被纯函数操作,纯函数一定不能修改原始数据结构或者产生副作用。
    函数式数据结构被定义为不可变的。
    模式匹配有点像一个别致的switch声明,它可以侵入到表达式的数据结构内部,对这个结构进行检验和提取子表达式。
    单向链表:

    sealed trait List[+A]
    
    case object Nil extends List[Nothing]
    
    case class Cons[+A](head: A, tail: List[A]) extends List[A]
    
    object List {
      def apply[A](as: A*): List[A] =
        if(as.isEmpty) Nil
        else Cons(as.head, apply(as.tail: _*))
    }
    

    练习 3.1
    求下面模式匹配的结果是什么?
    分析:分析列表匹配表达式3和4,但是匹配了3之后直接求值不会再匹配4,结果为1 + 2 = 3

      def sum(li: List[Int): Int = li match {
        case Nil => 0
        case Cons(h, t) => h + sum(t)  
      }
      
      val x = List(1, 2, 3, 4, 5) match {
        case Cons(x, Cons(2, Cons(4, _))) => x
        case Nil => 42
        case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
        case Cons(h, t) => h + sum(t)
        case _ => 101  
      }
    

    练习 3.2
    实现tail函数,删除一个List的第一个元素。
    分析:利用模式匹配提取列表的第一个元数据和子列表,然后返回子列表。

       def tail[A](li: List[A]): List[A] = li match {
        case Nil => Nil
        case Cons(h, t) => t  
      }
    

    练习 3.3
    实现函数setHead用一个不同的值代替列表中的第一个元素。
    分析:利用模式匹配提取第一个元素和子列表,用新的值和子列表组成一个新的列表并返回。

      def setHead[A](li: List[A], a: A): List[A] = li match {
        case Nil => Nil
        case Cons(h, t) => Cons(a, t)  
      }
    

    练习 3.4
    实现函数drop用于从列表中删除前n个元素。
    分析:利用模式匹配提取列表头部和尾部;利用递归完成元素的删除。

      def drop[A](li: List[A], n: Int): List[A] = n match {
        case m if m <= 0 => li
        case _ => li match {
          case Nil => Nil
          case Cons(h, t) => drop(t, n - 1)
        }
      }
    

    练习 3.5
    实现dropWhile函数,删除列表中前缀全部符合判定的元素。
    分析:利用模式匹配提取列表的头部元素和尾部列表,对头部元素进行判断,满足条件剔除元素;利用递归不断提取子列表的头部元素和其子元素完成列表的遍历。

      def dropWhile[A](li: List[A], f: A => Boolean): List[A] = li match {
        case Nil => Nil
        case Cons(h, t) =>
          if (f(h)) dropWhile(t, f)
          else Cons(h, dropWhile(t, f))
      }
    

    练习 3.6
    实现init函数,返回一个列表,它包含原列表除最后一个元素之外的所有的元素。
    分析:init函数实现困难些,递归的取列表的中的头元素,将取出的头元素组成新的列表,取到倒数第二个元素后,返回组成的新的列表。使用length函数获取列表的长度;使用append函数拼接列表以组成新的列表。

      def length[A](li: List[A]): Int = {
        def loop(li: List[A], acc: Int): Int = li match {
          case Nil => acc
          case Cons(_, t) => loop(t, acc + 1)
        }
        loop(li, 0)
      }
    
      def append[A](l1: List[A], l2: List[A]): List[A] = l1 match {
        case Nil => l2
        case Cons(h, t) => Cons(h, append(t, l2))
      }
    
      def init[A](li: List[A]): List[A] = {
        def loop(n: Int, li: List[A], res: List[A]): List[A] = n match {
          case m if m <= 1 => res
          case _ => li match {
            case Nil => Nil
            case Cons(h, t) => loop(n - 1, t, append(res, List(h)))
          }
        }
        loop(length(li), li, Nil)
      }
    

    练习 3.7

      def foldRight[A, B](li: List[A], b: B)(f: (A, B) => B): B = li match {
        case Nil => b
        case Cons(h, t) => f(h, foldRight(t, b)(f))  
      }
      
      def product(li: List[Double]): Double =
        foldRight(li, 1.0)(_ * _)
    
    1. 在入参是0.0时调用foldRight实现的product是否可以立即停止递归并返回0.0?为什么不可以或者为什么可以?
      分析:可以,foldRight是右折叠
    2. 对一个大的列表调用foldRight的时候会有什么问题发生?
      分析:性能问题,使用惰性列表来避免

    练习 3.8
    foldRight和List之间的关系,你有什么想法?
    分析:foldRight以递归的方式遍历List的每个元素,并将所有的元素进行聚合。

    练习 3.9
    使用foldRight实现计算List的长度。

      def length1[A](li: List[A]): Int =
        foldRight(li, 0){(_, b) =>
          b + 1
        }
    

    练习 3.10
    使用尾递归实现foldLeft

       def foldLeft[A, B](li: List[A], b: B)(f: (B, A) => B): B = li match {
        case Nil => b
        case Cons(h, t) => foldLeft(t, f(b, h))(f)
      }
    

    练习 3.11
    写下sum和product函数,和一个用foldLeft计算列表长度的函数

       def sum1(li: List[Int]): Int =
        foldLeft(li, 0)(_ + _)
    
      def product1(li: List[Double]): Double =
        foldLeft(li, 1.0)(_ * _)
    
      def length2[A](li: List[A]) =
        foldLeft(li, 0){(b, _) =>
          b + 1
        }
    

    练习 3.12
    写一个对原列表元素颠倒顺序的函数,看下是否可以用折叠实现。

      def reverse[A](li: List[A]): List[A] =
        foldLeft(li, Nil: List[A]){(b, a) =>
          Cons(a, b)
        }
    

    练习 3.13
    能否根据foldRight来实现foldLeft ?

      def foldLeft1[A, B](li: List[A], b: B)(f: (B, A) => B): B =
        foldRight(li, b){(a, b) =>
          f(b, a)
        }
    

    练习 3.14
    根据foldLeft或者foldRight实现append函数。

      def append1[A](l1: List[A], l2: List[A]): List[A] =
        foldLeft(l2, l1){(b, a) =>
          Cons(a, b)
        }
    

    练习 3.15
    写一个函数将一组列表连接成一个单个列表

      def union[A](li: List[List[A]]): List[A] =
        foldLeft(li, Nil: List[A]){(b, a) =>
          append(b, a)
        }
    

    练习 3.16

      def ++(li: List[Int]): List[Int] = li match {
        case Nil => Nil
        case Cons(h, t) => Cons(h + 1, ++(t))  
      }
    

    练习 3.17
    写一个函数,将List[Double]中的每个值转化成String

      def toString(li: List[Double]): List[String] =
        foldLeft(li, Nil: List[String]){(b, a) =>
          Cons(a.toString, b)
        }
    

    练习 3.18
    写一个泛化的map函数,对列表中每一个元素进行修改,并维持列表的结构。

     def map[A, B](li: List[A])(f: A => B): List[B] = li match {
       case Nil => Nil
       case Cons(h, t) => Cons(f(h), map(t)(f))
     }
     
     def map1[A, B](li: List[A])(f: A => B): List[B] =
       foldLeft(li, Nil: List[B]){(b, a) =>
         Cons(f(a), b)
       }
    

    练习 3.19
    写一个泛化的filter函数,从列表中删除所有不满足条件的元素。

      def filter[A](li: List[A])(f: A => Boolean): List[A] = li match {
        case Nil => Nil
        case Cons(h, t) =>
          if (f(h)) Cons(h, filter(t)(f))
          else filter(t)(f)
      }
    
      def filter1[A](li: List[A])(f: A => Boolean): List[A] =
        foldLeft(li, Nil: List[A]){(b, a) =>
          if (f(a)) Cons(a, b)
          else b
        }
    

    练习3.20

      def flatMap[A, B](li: List[A])(f: A => List[B]): List[B] = li match {
        case Nil => Nil
        case Cons(h, t) => append(f(h), flatMap(t)(f))
      }
    
      def flatMap1[A, B](li: List[A])(f: A => List[B]): List[B] =
        foldLeft(li, Nil: List[B]){(b, a) =>
          append(b, f(a))
        }
    

    练习 3.21
    用flatMap实现filter。

      def filter1[A](li: List[A])(f: A => Boolean): List[A] =
        flatMap(li){a =>
          if (f(a)) List(a)
          else Nil
        }
    

    练习 3.22
    写一个函数,接受两个列表,通过对应的元素相加构造出一个新的列表。

      def zip(l1: List[Int], l2: List[Int]): List[Int] = (l1, l2) match {
        case (a, Nil) => a
        case (Nil, b) => b
        case (Cons(h1, t1), Cons(h2, t2)) => Cons(h1 + h2, zip(t1, t2))
      }
    

    练习 3.23
    对刚才的函数泛化,不知针对整型和相加操作。

      def zipWith[A](l1: List[A], l2: List[A])(f: (A, A) => A): List[A] = (l1, l2) match {
        case (a, Nil) => a
        case (Nil, b) => b
        case (Cons(h1, t1), Cons(h2, t2)) => Cons(f(h1, h2), zipWith(t1, t2)(f))
      }
    

    练习 3.24

      def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = ???
    

    相关文章

      网友评论

          本文标题:函数式数据结构(1)

          本文链接:https://www.haomeiwen.com/subject/sddptftx.html