函数式的集合不允许 update value in place, 只能用函数操作集合,每个操作都产生新的集合。这样会造成大量copy吗? 令人惊异的是,No !
- 自己实现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函数调了很多次,会很浪费时间。