scala - working with List

In this post, we are going to examine the how to work with List in Scala, in this post we are going to examine the following.

  1. List literals
  2. homogeneous List
  3. The basic of List, head, tail, and isEmpty
  4. Divide and Conquer with List (implements the isort algorith)
  5. List operations - :: and :::
  6. Length of a List
  7. Reverse of a List
  8. Prefix and suffixes operation (such as dropWhile, takeWhile and etc...)
  9. List elements selection (the apply method and the indices methods)
  10. Flatten of a List 
  11. toString, mkString and the addString method on list
  12. List conversion, toArray, iterator, and copyToArray
  13. High-order method on List - map, flatMap, and foreach
  14. List filtering operations, -- filter, partition, find, takeWhile, dropWhile and span...
  15. Predicate over list - forall, and exists
  16. Fold operation, fold left /: and fold right :\ 
  17. Sorting list
  18. List range function (List object methods)
  19. List.fill and List.tabulate
  20. Scala's type inferences by example 

and below is the scala code by example with heavily comments. 

// working with lists.sclaa
//
// description: 
//  this is for working with lists


// - list literals
val fruit = List("apples", "oranges", "pears")
val nums =  List (1, 2, 3, 4)
val diag3 = 
  List(
    List(1, 0, 0),
    List(0, 1, 0),
    List(0, 0, 1)
  )
val empty = List()


// - list are homogeneous
//

val xs : List[String] = List()

// :: - List constructing . 
val fruit : List[String] = "apples" :: ("oranges" :: ("pears" :: (Nil))) // -- Nil is a speical form of List , stand for the Empty -elementes lists
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))

val empty = Nil // you can create an alias name for the Nil list. 


// head, tail, isEmpty 

Nil.head

// - the above show the following results. 
// java.util.NoSuchElementException: head of empty list




  
// -- implements the isort algorithms.
def isort(xs : List[Int]) : List[Int] = 
  if (xs.isEmpty) Nil
  else insert(xs.head, isort(xs.tail))

def insert(x :Int, xs : List[Int]) : List[Int] = 
  if (xs.isEmpty || x <= xs.head) x :: xs
  else xs.head :: insert(x, xs.tail)


// -- a fore-introduction to the extractor pattern and the constructor pattern such as a :: b
  
val fruit = List("apples", "oranges", "pears")  
// extractor pattern
val List(a, b, c) = fruits
// the constructor pattern,  x :: xs is treated as ::(x, xs), and indeed there is such an classes called 
//   '::'
// this is a pattern in scala, where 
// there is a '::" class in pacakge scala, and there is a '::' operator inside the list operator
val a :: b :: rest = fruits

// isort with the pattern matching
def isort(xs :List[Int]) : List[Int] = xs match {
    case List() => List() // List() , or the Nil
    case x :: xs1 => insert(x, isort(xs1))
  }

def insert(x : Int, xs : List[Int]) : List[Int] = xs match {
  case List() => List(x) // you can NOT write as follow 
  //case head :: xs1 if x <= head => x :: xs
  //case head :: xs1 if x > head => head :: insert(x, xs.tail)
  case head :: xs1 => if (x <= head) x :: xs 
                       else xs.head :: insert(x, xs1)
  
} 

isort(List(2, 5, 1, 7, 4, 0))


// next we will examine some first order method of the List operation

// ::: takes two list as operands
// -- list concatenation 
List(1,2 )  ::: List(3,4,5)

List() ::: List(1,2,3)

List(1,2,3,4) ::: List(1)


// divide and conquer methods


def append[T](xs : List[T], ys : List[T]) : List[T] = 
  xs match { 
  case List() => ys
  case x :: xs1 => x :: append(xs1, ys
}

// -- length of a list
List(1, 2, 3).length



// acccess the list in the reverse order
// -- init, last
val abcde  = List('a', 'b', 'c', 'd', 'e')

abcde.init
abcde.last


// -- reverse of a list

abcde.reverse

// -- write your own reverse function
def rev[T] (xs : List[T]) : List[T] = xs match { 
  case List() => xs
  case x :: xs1 => rev(xs1) ::: List(x)
}


//-- prefixes and suffixes
// the function that I will cover in this section include the following..

val abcde  = List('a', 'b', 'c', 'd', 'e')

abcde take  2

abcde drop 2
abcde splitAt 2


//-- elements selection
// the commands that will be covered in this section includ e
//   apply and indices
// NOTE:  rare in SCALA
// 
abcde apply 2
// is the same as 
abcde(2)

abcde.indices

// -- flatten of a list
List(List(1, 2), List(3), List(), List(4, 5)).flatten

val fruit = List("apple", "organge", "pear")
fruit.map(_.toCharArray).flatten
// you cannot apply "flatten" on a list whose element is not an list
List(1, 2, 3).flatten

//-- Zipping list, zip and unzip
//   zip
//   zipWithIndex
abcde.indices zip abcde

abcde.zipWithIndex

//-- toString and mkString
//   toString
//   mkString
//   addString (special form of mkString) , a method on the StrinbBuilder class
abcde.toString

abcde mkString ("[", ",", "]")
abcde mkString ("List(", ",", ")")

val buf = new StringBuilder
abcde addString (buf, "(", ",", ")")

//-- converting list
//   iterator
//   toArray
//   copyToArray
val arr = abcde.toArray
arr.toList


//-- copyToArray
val arr2 = new Array[Int](10)
List(1, 2, 3) copyToArray(arr2, 3)


val it = abcde.iterator
it.next
it.next // calling next to yield next result.


// List operator by example
//  MergeSort

def msort[T](less: (T, T) => Boolean) (xs : List[T]) : List[T] = {
  def merge (xs :List[T], ys : List[T]) : List[T] = 
    (xs, ys) match {
    case (Nil, _) => ys
    case (_, Nil) => xs
    case (x :: xs1, y :: ys1) => 
      if (less(x, y)) x :: merge(xs1, ys)
      else y :: merge(xs, ys1)
  }
  
  val n = xs.length / 2
  if (n == 0) xs
  else {
    val (ys, zs) = xs splitAt n
    merge(msort(less)(ys), msort(less)(zs))
  }
}
// call with the following methods
//    msort((x : Int, y : Int) => if (x > y) true else false)( List(1, 5, 2, 3, 4,8,6))
// or 
//  msort((x : Int, y : Int) => x < y)( List(1, 5, 2, 3, 4,8,6))

// -- high order method on List
//   map
//   flatMap
//   foreach
// e.g.
// 
List(1, 2, 3) map (_ + 1)

val words = List("the", "quick", "brown", "fox")
words map(_.length)

// map vs. flatMap
words map(_.toList)
words flatMap(_.toList)

List.range(1, 5) flatMap(i => List.range(1, i) map (j => (i , j)))

var sum = 0
List(1, 2, 3, 4, 5) foreach (sum += _)

sum


// filtering lists
//  filter, partition, find, takeWhile, dropWhile, and span
//  filter
//  partition,
//  find,
//  takeWhile
//  dropWhile,
//  span

List(1, 2, 3, 4, 5) filter (_ % 2 == 0)
words filter (_.length == 3)
List(1, 2,3, 4, 5) partition (_ % 2 == 0) // partition equals (xs filter p, xs filter (!p(_))
List(1, 2,3, 4, 5) find (_ % 2 == 0) // returns an Option, or Some(...)
List(1, 2,3, -4, 5) takeWhile (_ > 0)
words dropWhile (_ startsWith "t")
List(1, 2,3, -4, 5) span (_ > 0) // xs span p equals (xs takeWhile p, xs dropWhile p)

// predicate over lists : forall and exists
def hasZeroRow(m : List[List[Int]]) = 
  m exists (row => row forall (_ == 0))

val diag3 = List[List[Int]](List(1, 2, 3), List(0, 0, 0), List(4, 5, 6))
hasZeroRow(diag3) // you cannot do hasZeroRow diag3, because this will be translated to "hasZero.diag3"

// folding lists: /: and :\
//  /:
//  :\
//  and an e.g.
//   sum(List(a, b, c)) equals 0 + a + b + c
def sum(xs : List[Int]) : Int = (0 /: xs) (_ + _) // check on how the "/:" operator is defined?? 
def product(xs : List[Int]) : Int = (1 /: xs) (_ * _) // looks like that, it has defined some object, or funciton on the primitive
                                                      // type such as "Int, Float"

// e.g.
// def reverseLeft[T](xs : List[T]) = (startvalue /: xs) (operation)
def reverseLeft[T](xs : List[T]) = (List[T]() /: xs) { (ys, y) => y :: ys }

// sorting List
//   sortWith
List(1, -3, 4, 2, 6) sortWith (_ < _)
words sortWith (_.length > _.length)

// -- Method of the List object
//  as We all know that List has a List class and List also has a 
//  List object
// 
List(1, 2, 3) // equivalent to the following.
List.apply(1, 2, 3) // which is a method of the List companion object

// List.Range
List.range(1, 5) // as 1 to 5
List.range(1, 9, 2)
List.range(9, 1, -3)


// List.fill
List.fill(5)('a')
List.fill(2, 3)('b') // and you can have multiple dimension with 'fill' method

// List.Tabulate
val squares = List.tabulate(5)( n => n * n)
val multiplication = List.tabulate(5, 5)(_ * _) // two dimension , i<- 0 to 5, j <- 0 to 5, i * j

//-- Processing multiple list together.
(List(10, 20), List(3, 4, 5)).zipped.map(_ * _) // longer elements are truncated.
(List("abc", "de"), List(3, 2)).zipped forall (_.length == _)
(List("abc", "de"), List(3, 2)).zipped exists(_.length != _)

//-- understanding the type inference system in Scala
// considering the following e.g.
msort((x : Char, y : Char) => x > y ) (abcde)
abcde sortWith(_ > _)

msort(_ > _)(abcde) // <console>:10: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$greater(x$2))
// type system in Scala is flow-based


// however, if we swap the argument of the two, then we will ends up having this.
def msortSwapped[T](xs : List[T])(less: (T, T) => Boolean) : List[T] = {
  def merge (xs :List[T], ys : List[T]) : List[T] = 
    (xs, ys) match {
    case (Nil, _) => ys
    case (_, Nil) => xs
    case (x :: xs1, y :: ys1) => 
      if (less(x, y)) x :: merge(xs1, ys)
      else y :: merge(xs, ys1)
  }
  
  val n = xs.length / 2
  if (n == 0) xs
  else {
    val (ys, zs) = xs splitAt n
    merge(msort(less)(ys), msort(less)(zs))
  }
}

msortSwapped(abcde)(_ > _) // why this works?, this is because, with the msort(abcde), we know it return a partially applied 
                    // function which accepts (T, T) => Boolean and return a type of List[T], and abcde has type 
                    // List[T], with the flow deduction, it can map to the real argument type, which is Char (because
                    // abcde is of type List[Char])


// NOTE: a rule of thumb
//   when designing a polymorphic method that takes some non-function arguments and a function arguments, place the function argument last in a curried 
// parameter by its own

你可能感兴趣的:(scala)