Play scala 4 List的实现

函数式的集合不允许 update value in place, 只能用函数操作集合,每个操作都产生新的集合。这样会造成大量copy吗? 令人惊异的是,No !

  1. 自己实现List

package ch3.dslist

sealed trait List[+A]  //sealed的作用: 所有继承List的类型都必须定义在本文件

case object Nil extends List[Nothing]

case class Cons[+A](head: A, tail: List[A]) extends List[A]

object List {
  // 求和函数
  def sum(ints: List[Int]): Int =  ints match {
    case Nil => 0
    case Cons(x, xs) => x + sum(xs)   // 用构造器作模式匹配
  }
  
  // 求积函数,要求只要其中一个为0就立即返回0
  def product(nums: List[Double]) : Double = nums match {
    case Nil => 1.0
    case Cons(0.0, xs) => 0
    case Cons(x, xs) => x * product(xs)
  }
  
  // 创建List的方法
  def apply[A](as: A*):List[A] = {
    if (as.isEmpty) Nil
    else Cons(as.head, apply(as.tail: _*))
  }
}


测试

package ch3.dslist

object testList {
  println("Welcome to the Scala worksheet")       //> Welcome to the Scala worksheet
  val example = Cons(1, Cons(2, Cons(3, Nil)))    //> example  : ch3.dslist.Cons[Int] = Cons(1,Cons(2,Cons(3,Nil)))
  val example2 = List(1.0, 2.0, 3.0)              //> example2  : ch3.dslist.List[Double] = Cons(1.0,Cons(2.0,Cons(3.0,Nil)))
  List.sum(example)                               //> res0: Int = 6
  List.product(example2)                          //> res1: Double = 6.0
}

2. 添加一下常用操作

  
  def tail[A](l: List[A]): List[A] = l match {
    case Nil => throw new Error("Nil.tail")
    case Cons(x, xs) => xs
  }
  
  def drop[A] (l: List[A], n: Int): List[A] = {
    if (n == 1) tail(l)
    else drop(tail(l), n-1)
  }
  
  def dropWhile[A](l: List[A])(f: A => Boolean): List[A] = l match {
    case Nil => Nil
    case Cons(x, xs) => 
      if (f(x)) dropWhile(xs)(f)
      else Cons(x, dropWhile(xs)(f))
  }
  
  def setHead[A](l: List[A], h: A): List[A] = Cons(h, l)
  
  // O(N)
  def append[A](a1: List[A], a2: List[A]): List[A] = a1 match {
    case Nil => a2
    case Cons(x, xs) => Cons(x, append(xs, a2))
  }

这些操作并没有大量copy,而是复用了内存。因为整体是immutable的,所有部分也是immutable的。当call tail时,只需将tail的引用返回就好了。不用担心以后因原来的list有变化,而影响作为返回值的list。

这个世界本来就应该immutable和muttable共存。因为有些操作中immutable的集合上操作更高效,比如tail,append。有些操作中muttable的集合上操作更高效,比如update一个元素


3.实现init函数,返回除最后一个元素之外,其它元素按顺序组成的List

  def init[A](l: List[A]): List[A] = l match {
    case Nil => Nil
    case Cons(x, xs) => 
      xs match {
        case Nil => List()
        case Cons(y, ys) => Cons(x, init(xs))
      }
  }

4. 实现foldRight函数。并用之改进sum和product

  def foldRight[A, B](l: List[A], z: B)(f: (A, B) => B): B = l match {
    case Nil => z
    case Cons(x, xs) => f(x, foldRight(xs, z)(f))
  }
  
  def sum2(l: List[Int]) = foldRight(l, 0)(_ + _)
  
  def product2(l: List[Double]) = foldRight(l, 1.0)(_ * _)

5. 用foldRight实现length函数

def length[A](l: List[A]): Int = foldRight(l, 0)((a ,b) => b +1)

6. 我们的foldRight函数的实现不是尾递归的,当List很大时容易造成stackOverflow。现在用尾递归实现一个foldLeft函数,并重新实现sum 和 product。

  def foldLeft[A, B](l: List[A], z: B)(f: (A, B) => B): B = {
     def ff = 0
     def loop(a : B, m:List[A]): B = m match {
       case Nil => a
       case Cons(x, xs) => loop(f(x, a), xs)
     }
    loop(z, l)
  }
  def sum3(l: List[Int]) = foldLeft(l, 0)(_ + _)
  def product3(l: List[Double]) = foldLeft(l, 1.0) (_ * _)

将非尾递归改为尾递归的一般方法: 用循环结构重新实现算法,把这个循环结构写成新的递归


7.Write a function that transforms a list of integers by adding  1 to  each  element.  (Reminder:  this  should  be  a  pure  function  that  returns  a  new  !) List 

给每个元素都加1

  def addOne(l: List[Int]): List[Int] = l match {
    case Nil => Nil
    case Cons(x, xs) => Cons(x +1, addOne(xs))
  }

8.  Write a function that turns each value in a  List[Double] into a  . String

将Double转化为String

  def all2String(l:List[Double]): List[String] = l match {
    case Nil => Nil
    case Cons(x, xs) => Cons(x.toString, all2String(xs))
  }

9. 实现map函数

  def map[A, B](l: List[A])(f: A => B): List[B] = l match {
    case Nil => Nil
    case Cons(x, xs) => Cons(f(x), map(xs)(f))
  }

10. Write a function  that removes elements from a list filter unless they satisfy a given predicate. Use it to remote all odd numbers from a . List[Int]

实现filter函数,并用它筛去List[Int]中的所有奇数

  def filter[A](l: List[A])(f: A => Boolean): List[A] = l match {
    case Nil => Nil
    case Cons(x, xs) =>
      if (f(x))  Cons(x, filter(xs)(f))
      else filter(xs)(f)
  }

11. 实现flatMap

  def flatMap[A, B](l: List[A])(f: A => List[B]): List[B] = l match {
    case Nil => Nil
    case Cons(x, xs) => 
      f(x) match {
        case Nil => flatMap(xs)(f)
        case Cons(y, ys) => append(f(x), flatMap(xs)(f))
      }
  }

12. 用flatMap实现filter

 def filter2[A](l: List[A])(f: A => Boolean): List[A] = flatMap(l)(  x => if (f(x)) List(x)  else Nil)

13. 实现向量加法

比如:add(List(1,2,3), List(2, 3, 4)) = List(3, 5, 7)

  def head[A](l: List[A]): A = l match {
    case Nil => throw new Error("Nil.head")
    case Cons(x, xs) => x
  }
  
  def add(a1: List[Int], a2: List[Int]): List[Int] = a1 match {
    case Nil => Nil
    case Cons(x, xs) => Cons(x + head(a2), add(xs, tail(a2)))
  }

14. 判断一个序列是不是另一序列的子序列:def hasSubsequence[A](l: List[A], sub: List[A]): Boolean

我们用最自然的算法判断:拿第二个list与第一个list从头开始比较,如果不相等,就从第二个元素开始比较。

这个算法显然要依赖另外两个函数:1. equals函数,用于比较两个list  2. take函数,用于获取前n个元素

 def take[A](l: List[A], n: Int): List[A] =  l match {
    case Nil => 
      if (n == 0) Nil
      else throw new Exception("No element")
    case Cons(x, xs) =>
      if (n >= 1) Cons(x, take(xs, n-1))
      else Nil
  }
  
  def equals[A](a: List[A], b: List[A]): Boolean = a match {
    case Nil => 
      b match {
        case Nil => true
        case _ => false
      }
    case Cons(x, xs) => 
      b match {
        case Nil => false
        case Cons(y, ys) => 
          if (x == y) equals(xs, ys)
          else false
      }
  }
  
  def hasSubsequence[A](a: List[A], b: List[A]): Boolean = {
    if (length(a) < length(b)) false
    else {
      val c = take(a, length(b))
      if (equals(c, b)) true
      else hasSubsequence(tail(a), b)
    }
  }

显然,上述代码有很多地方可以优化,比如length函数和take函数调了很多次,会很浪费时间。

你可能感兴趣的:(scala)