您的位置:首页 > 其它

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)
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: