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.