Functional Programming in Scala第三章练习

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

}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值