Functional Programming in Scala第三章练习
2015-06-24 00:18
381 查看
学习scala的练习,同学们请多多指教
package com.hotcharm.www import scala.annotation.tailrec object Chapter3 { 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 isEmpty[A](as: List[A]): Boolean = as match { case Nil => true case _ => false } def head[A](as: List[A]): A = as match { case Nil => throw new Exception("list is empty, no head!") case Cons(x, xs) => x } def sum(ints: List[Int]): Int = ints match { case Nil => 0 case Cons(x, xs) => x + sum(xs) } def product(ds: List[Double]): Double = ds match { case Nil => 1.0 case Cons(0.0, _) => 0.0 case Cons(x, xs) => x * product(xs) } def apply[A](as: A*): List[A] = if (as.isEmpty) Nil else Cons(as.head, apply(as.tail: _*)) /** * EXERCISE 3.2 * Implement the function tail for removing the first element of a List. Note that the * function takes constant time. What are different choices you could make in your * implementation if the List is Nil? We’ll return to this question in the next chapter. */ def tail[B](lst: List[B]): List[B] = lst match { case Nil => Nil case Cons(_, xs) => xs } /** * EXERCISE 3.4 * Generalize tail to the function drop, which removes the first n elements from a list. * Note that this function takes time proportional only to the number of elements being * dropped—we don’t need to make a copy of the entire List. */ @annotation.tailrec def drop[A](l: List[A], n: Int): List[A] = { if (n <= 1) tail(l) else drop(tail(l), n - 1) } /** * EXERCISE 3.5 * Implement dropWhile, which removes elements from the List prefix as long as they * match a predicate. */ @annotation.tailrec def dropWhile[A](l: List[A], f: A => Boolean): List[A] = l match { case Nil => Nil case Cons(h, t) => if (f(h)) dropWhile(t, f) else l } /** * EXERCISE 3.6 * Not everything works out so nicely. Implement a function, init, that returns a List * consisting of all but the last element of a List. So, given List(1,2,3,4), init will * return List(1,2,3). Why can’t this function be implemented in constant time like * tail? */ def init[A](l: List[A]): List[A] = reverse(tail(reverse(l))) def reverse[A](l: List[A]): List[A] = { @annotation.tailrec def reverseIter[A](l: List[A], rl: List[A]): List[A] = l match { case Nil => rl case Cons(h, t) => reverseIter(t, Cons(h, rl)) } reverseIter(l, Nil) } /** * EXERCISE 3.7 * Can product, implemented using foldRight, immediately halt the recursion and * return 0.0 if it encounters a 0.0? Why or why not? Consider how any short-circuiting * might work if you call foldRight with a large list. This is a deeper question that we’ll * return to in chapter 5. */ /** * EXERCISE 3.9 * Compute the length of a list using foldRight. * def length[A](as: List[A]): Int */ def length[A](as: List[A]): Int = foldRight(as, 0)((_, cnt) => cnt + 1) /** * EXERCISE 3.10 * Our implementation of foldRight is not tail-recursive and will result in a StackOverflowError * for large lists (we say it’s not stack-safe). Convince yourself that this is the * case, and then write another general list-recursion function, foldLeft, that is * tail-recursive, using the techniques we discussed in the previous chapter. Here is its * signature: * def foldLeft[A,B](as: List[A], z: B)(f: (B, A) => B): B */ @annotation.tailrec def foldLeft[A, B](as: List[A], z: B)(f: (B, A) => B): B = as match { case Nil => z case Cons(x, xs) => foldLeft(xs, f(z, x))(f) } /** * EXERCISE 3.12 * Write a function that returns the reverse of a list (given List(1,2,3) it returns * List(3,2,1)). See if you can write it using a fold. */ def reverse2[A](as: List[A]): List[A] = foldLeft(as, List[A]())((l, b) => Cons(b, l)) /** * EXERCISE 3.13 * Hard: Can you write foldLeft in terms of foldRight? How about the other way * around? Implementing foldRight via foldLeft is useful because it lets us implement * foldRight tail-recursively, which means it works even for large lists without overflowing * the stack. */ def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = { foldLeft(reverse(as), z)((a, b) => f(b, a)) } /** * EXERCISE 3.14 * Implement append in terms of either foldLeft or foldRight. */ def append[A](as1: List[A], as2: List[A]): List[A] = { foldRight(as1, as2)(Cons(_, _)) } /** * EXERCISE 3.15 * Hard: Write a function that concatenates a list of lists into a single list. Its runtime * should be linear in the total length of all lists. Try to use functions we have already * defined. */ def concatLists[A](ass: List[List[A]]) = { foldLeft(ass, List[A]())((z, as) => append(z, as)) } /** * EXERCISE 3.18 * Write a function map that generalizes modifying each element in a list while maintaining * the structure of the list. Here is its signature:12 * def map[A,B](as: List[A])(f: A => B): List[B] */ def map[A, B](as: List[A])(f: A => B): List[B] = { foldRight(as, List[B]())((a, z) => Cons(f(a), z)) } /** * EXERCISE 3.16 * 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!) */ def allAddOne(l: List[Int]): List[Int] = { map(l)((x: Int) => (x + 1)) } /** * EXERCISE 3.17 * Write a function that turns each value in a List[Double] into a String. You can use * the expression d.toString to convert some d: Double to a String. */ def doubleLst2StrLst(l: List[Double]): List[String] = { map(l)((x: Double) => x.toString) } /** * EXERCISE 3.19 * Write a function filter that removes elements from a list unless they satisfy a given * predicate. Use it to remove all odd numbers from a List[Int]. * def filter[A](as: List[A])(f: A => Boolean): List[A] */ def filter[A](as: List[A])(f: A => Boolean): List[A] = { foldRight(as, List[A]())((a, z) => (if (f(a)) Cons(a, z) else z)) } /** * EXERCISE 3.20 * Write a function flatMap that works like map except that the function given will return * a list instead of a single result, and that list should be inserted into the final resulting * list. Here is its signature: * def flatMap[A,B](as: List[A])(f: A => List[B]): List[B] * For instance, flatMap(List(1,2,3))(i => List(i,i)) should result in * List(1,1,2,2,3,3). */ def flatMap[A, B](as: List[A])(f: A => List[B]): List[B] = concatLists(map(as)(f)) /** * EXERCISE 3.21 * Use flatMap to implement filter. */ def filter2[A](as: List[A])(f: A => Boolean): List[A] = { flatMap(as)(x => if (f(x)) List(x) else Nil) } /** * EXERCISE 3.22 * Write a function that accepts two lists and constructs a new list by adding corresponding * elements. For example, List(1,2,3) and List(4,5,6) become List(5,7,9). */ def zipAddIntLists(as1: List[Int], as2: List[Int]): List[Int] = { zipWith(as1, as2, (x: Int, y: Int) => (x + y)) } /** * EXERCISE 3.23 * Generalize the function you just wrote so that it’s not specific to integers or addition. * Name your generalized function zipWith. */ def zipWith[A](as1: List[A], as2: List[A], f: (A, A) => A): List[A] = { @annotation.tailrec def zipIter(as1: List[A], as2: List[A], r: List[A]): List[A] = { if (isEmpty(as1) || isEmpty(as2)) r else zipIter(tail(as1), tail(as2), Cons(f(head(as1), head(as2)), r)) } reverse(zipIter(as1, as2, List[A]())) } def take[A](as: List[A], n: Int): List[A] = { @annotation.tailrec def takeIter[A](as: List[A], n: Int, r: List[A]): List[A] = { if (n <= 0 || isEmpty(as)) r else takeIter(tail(as), n - 1, Cons(head(as), r)) } reverse(takeIter(as, n, List[A]())) } /** * EXERCISE 3.24 * Hard: As an example, implement hasSubsequence for checking whether a List contains * another List as a subsequence. For instance, List(1,2,3,4) would have * List(1,2), List(2,3), and List(4) as subsequences, among others. You may have * some difficulty finding a concise purely functional implementation that is also efficient. * That’s okay. Implement the function however comes most naturally. We’ll * return to this implementation in chapter 5 and hopefully improve on it. Note: Any * two values x and y can be compared for equality in Scala using the expression x == y. * def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean */ def hasSubsequence[A](sup: List[A], sub: List[A]): Boolean = { val supLen = length(sup) val subLen = length(sub) val ls = take(sup, subLen) if (supLen >= subLen) (ls == sub) || hasSubsequence(tail(sup), sub) else false } } sealed trait Tree[+A] case class Leaf[A](value: A) extends Tree[A] case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A] object Tree { /** * EXERCISE 3.25 * Write a function size that counts the number of nodes (leaves and branches) in a tree. */ def size[A](t: Tree[A]): Int = t match { case Leaf(_) => 1 case Branch(l, r) => 1 + size(l) + size(r) } /** * EXERCISE 3.26 * Write a function maximum that returns the maximum element in a Tree[Int]. (Note: * In Scala, you can use x.max(y) or x max y to compute the maximum of two integers x * and y.) */ def maximum(t: Tree[Int]): Int = t match { case Leaf(v) => v case Branch(l, r) => maximum(l) max maximum(r) } /** * EXERCISE 3.27 * Write a function depth that returns the maximum path length from the root of a tree * to any leaf. */ def depth[A](t: Tree[A]): Int = t match { case Leaf(_) => 1 case Branch(l, r) => 1 + (depth(l) max depth(r)) } /** * EXERCISE 3.28 * Write a function map, analogous to the method of the same name on List, that modifies * each element in a tree with a given function. */ def map[A, B](t: Tree[A], f: A => B): Tree[B] = t match { case Leaf(v) => Leaf(f(v)) case Branch(l, r) => Branch(map(l, f), map(r, f)) } /** * EXERCISE 3.29 * Generalize size, maximum, depth, and map, writing a new function fold that abstracts * over their similarities. Reimplement them in terms of this more general function. Can * you draw an analogy between this fold function and the left and right folds for List? */ def fold[A, B](t: Tree[A], fl: (A) => B, fb: (B, B) => B): B = t match { case Leaf(v) => fl(v) case Branch(l, r) => fb(fold(l, fl, fb), fold(r, fl, fb)) } def size2[A](t: Tree[A]): Int = fold(t, (v: A) => 1, (i: Int, j: Int) => i + j + 1) def maximum2(t: Tree[Int]): Int = fold(t, (v: Int) => v, (i: Int, j: Int) => if (i >= j) i else j) def depth2[A](t: Tree[A]): Int = fold(t, (v: A) => 1, (i: Int, j: Int) => 1 + (i max j)) def map2[A, B](t: Tree[A], f: A => B): Tree[B] = fold(t, (v: A) => Leaf(f(v)), (t1: Tree[B], t2: Tree[B]) => Branch(t1, t2)) } }
package com.hotcharm.www import org.scalatest._ import Chapter3._ import Chapter3.List._ class TestChapter3 extends FunSuite { 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 } test("x is first add second") { assert(x == 3) } test("drop") { val l1: List[Int] = List(1, 3, 5, 4, 6) assert(drop(l1, 2) == List(5, 4, 6)) } test("dropWhile") { val l: List[Int] = List(1, 3, 5, 4, 6) assert(dropWhile(l, (x: Int) => x < 4) == List(5, 4, 6)) assert(dropWhile(l, (x: Int) => x > 7) == l) } test("reverse") { val l3: List[Int] = List(1, 3, 5) assert(reverse(l3) == List(5, 3, 1)) } test("init") { val l4 = List(1, 3, 4, 6) assert(init(l4) == List(1, 3, 4)) } /* * EXERCISE 3.8 * See what happens when you pass Nil and Cons themselves to foldRight, like this: * foldRight(List(1,2,3), Nil:List[Int])(Cons(_,_)). What do you think this * says about the relationship between foldRight and the data constructors of List? */ test("EXERCISE 3.8") { val l5 = List(1, 2, 3) val l6 = foldRight(l5, Nil: List[Int])(Cons(_, _)) println(l6) } test("length") { val l7 = List(1, 2, 3, 4) val l8 = Nil assert(length(l7) == 4) assert(length(l8) == 0) } test("foldLeft tail rec") { val l9 = List(1, 2, 3, 4, 5) assert(foldLeft(l9, 0)(_ + _) == 15) } test("reverse2 use fold") { val l10 = List(1, 2, 3, 4, 5) val l11 = List(5, 4, 3, 2, 1) assert(reverse2(l10) == l11) } test("append") { val l10 = List(1, 2) val l11 = List(5, 4, 3) assert(append(l10, l11) == List(1, 2, 5, 4, 3)) } test("concatLists") { val l10 = List(1, 2) val l11 = List(5, 4, 3) val lss = List(l10, l11) assert(concatLists(lss) == List(1, 2, 5, 4, 3)) } test("allAddOne") { val l = List(1, 3, 5) assert(allAddOne(l) == List(2, 4, 6)) } test("doubleLst2StrLst") { val l = List(1.0, 3.4, 5.6) val l2 = List("1.0", "3.4", "5.6") assert(doubleLst2StrLst(l) == l2) } test("filter") { val l = List(1, 2, 5, 4, 3) assert(filter(l)(_ % 2 == 0) == List(2, 4)) } test("flatMap") { assert(flatMap(List(1, 2, 3))(i => List(i, i)) == List(1, 1, 2, 2, 3, 3)) } test("filter2") { val l = List(1, 2, 5, 4, 3) assert(filter2(l)(_ % 2 == 0) == List(2, 4)) } test("zipAddIntLists") { assert(zipAddIntLists(List(1, 2, 3), List(4, 5, 6)) == List(5, 7, 9)) assert(zipAddIntLists(List(1, 2, 3), List(4, 5)) == List(5, 7)) } test("hasSubsequence") { assert(hasSubsequence(List(1, 3, 5, 6, 7), List(3, 5, 6))) assert(!hasSubsequence(List(1, 3, 5, 6, 7), List(13, 5, 6))) } test("tree size") { val t = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Leaf(4)))) assert(Tree.size(t) == 7) assert(Tree.size2(t) == 7) } test("tree maximum") { val t = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Leaf(4)))) assert(Tree.maximum(t) == 4) assert(Tree.maximum2(t) == 4) } test("tree depth") { val t = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Leaf(4)))) assert(Tree.depth(t) == 4) assert(Tree.depth2(t) == 4) } test("tree map"){ val t = Branch(Leaf(1), Branch(Leaf(2), Branch(Leaf(3), Leaf(4)))) val t1 = Branch(Leaf("1"), Branch(Leaf("2"), Branch(Leaf("3"), Leaf("4")))) assert(Tree.map(t, (x:Int) => x.toString()) == t1) assert(Tree.map2(t, (x:Int) => x.toString()) == t1) } }
相关文章推荐
- ssh添加公钥
- ssh添加公钥
- 随堂笔记
- 创建和使用静态库(问题:undefined reference)
- 20个2014年最优秀的PHP框架
- SGU 299.Triangle
- 传智播客--20150623iOS开发前奏
- 字符和数字互转
- 化忧愁于努力
- redis client protocol 解析
- leetcode-Partition List
- leetcode-Partition List
- Gson 进行 json 数据的相关的操作
- libevent源码深度剖析一
- 引用类型1——JS学习笔记2015-6-23(第64天)
- unity5, UI Button "On Button Down"
- 20150624 lintcode 总结 Validate Binary Search Tree
- LocalBroadcastManager局部广播管理器
- Java基础 String类的一个小问题
- MySQL 数据类型