Scala 函数式程序设计原理(5)--Lists

Posted on 2017-05-25 14:11  paulingzhou  阅读(249)  评论(0编辑  收藏  举报

5.1 More Functions on Lists

xs.length  The number of elements of xs

xs.last    The List's last element, exception if xs is empty

xs.init    A list consisting of all elements of xs except the last one, exception if xs is empty

xs take n  A list consisting of the first n elements of xs, or xs itself it is shorter than n

xs drop n  The rest of the collection after taking n elements

xs(n)    (or, written out, xs apply n). The element of xs at index n.

 

Creating new lists:

xs ++ ys      The list consisting of all elements of xs followed by all elements of ys

xs.reverse      The list containing the elements of xs in reversed order

xs updated (n, x)  The list containing the same elements as xs, except at index n where it contains x.

 

Finding elements:

xs indexOf x    The index of the first element in xs equal to x, or -1 if x does not appear in xs

xs contains x    same as xs indexOf x >= 0

 

5.2 Pairs and Tuples

A tuple type (t1, ..., tn) is an abbreviation of the parameterized type scala.Tuplen[t1, ..., tn]

A tuple expression (e1, ..., en) is equivalent to the function application scala.Tuplen(e1, ..., en)

 

5.3 Implicit Parameters

scala.math.Ordering[T] : a class in the standard library that represents orderings.

Rules for Implicit Parameters:

Say, a function takes an implicit parameter of type T.

The compiler will search an implicit definition that

  • is marked implicit
  • has a type compatible with T
  • is visible at the point of the function call, or is defined in a companion object associated with T.

 

5.4 Higher-Order List Functions

Map:

This scheme can be generalized to the method map of the List class.

A simple way to define map is as follows:

abstract class List[T] { ...
  def map[U](f: T => U): List[U] = this match {
    case Nil    => this
    case x :: xs  => f(x) :: xs.map(f)
  }
}

Filter:

This pattern is generalized by the method filter of the List class:

abstract class List[T] {
  ...
  def filter(p: T => Boolean): List[T] = this match {
    case Nil    => this
    case x :: xs  => if(p(x)) x :: xs.filter(p) else xs.filter(p)
  }
}

 Using filter, posElems can be writtern more concisely.

def posElems(xs: List[Int]): List[Int] =
  xs filter (x => x > 0)

 other higher order functions:

object listfun {
  val nums = List(2, -4, 5, 7, 1)                 //> nums  : List[Int] = List(2, -4, 5, 7, 1)
  val fruits = List("apple", "pineapple", "orange", "banana")
                                                  //> fruits  : List[String] = List(apple, pineapple, orange, banana)
  
  nums filter (x => x > 0)                        //> res0: List[Int] = List(2, 5, 7, 1)
	nums filterNot (x => x > 0)               //> res1: List[Int] = List(-4)
	nums partition (x => x > 0)               //> res2: (List[Int], List[Int]) = (List(2, 5, 7, 1),List(-4))
	nums takeWhile (x => x > 0)               //> res3: List[Int] = List(2)
	nums dropWhile (x => x > 0)               //> res4: List[Int] = List(-4, 5, 7, 1)
	nums span (x => x > 0)                    //> res5: (List[Int], List[Int]) = (List(2),List(-4, 5, 7, 1))

}

 

5.5 Reduction of Lists

reduceLeft:

def sum(xs: List[Int])    = (0::xs) reduceLeft (_ + _)
def product(xs: List[Int])  = (1::xs) reduceRight (_ * _)

foldLeft:

foldLeft is like reduceLeft but takes an accumulator, z, as an additional parameter, which is returned when foldLeft is called on an empty list.

(List(x1, ..., xn) foldLeft z)(op) = (...(z io x1) op ... ) op xn

 so sum and product can also be defined as follows:

def sum(xs: List[Int])     = (xs foldLeft 0) (_ + _)
def product(xs: List[Int])  = (xs foldLeft 1) (_ * _) 

 implementation:

abstract class List[T] { ...
  def reduceLeft(op: (T, T) => T):T = this match {
    case Nil => throw new Error("Nil.reduceLeft")
    case x::xs => (xs foldLeft x)(op)
  }

  def foldLeft[U](z: U)(op: (U, T) => U): U = this match {
    case Nil => z
    case x:: xs => (xs foldLeft op(z, x))(op)
  }
}

 

FoldRight and ReduceRight:

Applications of foldLeft and reduceLeft unfold on trees that lean to the left.

They have two dual functions, foldRight and reduceRight, which produce trees which lean to the right, i.e.,

List(x1, ..., x{n-1}, xn) reduceRight op = x1 op ( ... (x{n-1} op xn) ... )
(List(x1, ..., xn) foldRight acc)(op) = x1 op ( ... (xn op acc) ... )

implementation:

def reduceRight(op: (T, T) => T): T = this match {
  case Nil => throw new Error("Nil.reduceRight")
  case x :: Nil => x
  case x :: xs => op(x, xs.reduceRight(op))
}
def foldRight[U](z: U)(op: (T, U) => U): U = this match {
  case Nil => z
  case x::xs => op(x, (xs foldRight z)(op))
}

 

Difference between FoldLeft and FoldRight:

For operators that are associative and commutative, foldLeft and foldRight are equivalent (even though there may be a difference in efficiency)

But sometimes, only one of the two operators is appropriate.

 

implemention of mapFun and lengthFun using foldRight:

	def mapFun[T, U] (xs: List[T], f: T=> U): List[U] = (xs foldRight List[U]())(f(_) :: _)
                                                  //> mapFun: [T, U](xs: List[T], f: T => U)List[U]
	mapFun[Int, Int](nums, x => x + 1)        //> res7: List[Int] = List(3, -3, 6, 8, 2)
	
	def lengthFun[T](xs: List[T]): Int = (xs foldRight 0) ((_,acc) => acc+1)
                                                  //> lengthFun: [T](xs: List[T])Int
	lengthFun(nums)                           //> res8: Int = 5

 

5.6 Reasoning About Concat

Structual induction:

To prove a property P(xs) for all lists xs,

  • show that P(Nil) holds (base case),
  • for a list xs and some element x, show the induction step:  if P(xs) holds, then P(x::xs) also holds.