Express in a functional style with pattern matching
- def printList (xs:List[Int]) : Unit =
- val xs = List(11,21,31)
- printList (xs)
Visit all elements of a list
- def printList (xs:List[Int]) : Unit = xs match
- case Nil => ()
- case y::ys =>
- printList (ys)
- val xs = List(11,21,31)
- printList (xs)
Print an element when visiting
- def printList (xs:List[Int]) : Unit = xs match
- case Nil => ()
- case y::ys =>
- println (y)
- printList (ys)
- val xs = List(11,21,31)
- printList (xs)
Format every element before printing
- def printList (xs:List[Int]) : Unit = xs match
- case Nil => ()
- case y::ys =>
- println ("0x%02x".format(y))
- printList (ys)
- val xs = List(11,21,31)
- printList (xs)
Generalize the idea of processing every element
- def foreach (xs:List[Int], f:Int=>Unit) : Unit = xs match
- case Nil => ()
- case y::ys =>
- f (y)
- foreach (ys, f)
- val xs = List(11,21,31)
- foreach (xs, println)
Customize with changed function argument
- def foreach (xs:List[Int], f:Int=>Unit) : Unit = xs match
- case Nil => ()
- case y::ys =>
- f (y)
- foreach (ys, f)
- def printHex (x:Int) = println("0x%02x".format(x))
- val xs = List(11,21,31)
- foreach (xs, printHex)
Generalize the type of list with parameters
- def foreach [X] (xs:List[X], f:X=>Unit) : Unit = xs match
- case Nil => ()
- case y::ys =>
- f (y)
- foreach (ys, f)
- def printLength (xs:List[Int]) = println (xs.length)
- val xss = List(List(11,21,31),List(),List(41,51))
- foreach (xss, printLength)
Use a lambda expression (anonymous function)
- def foreach [X] (xs:List[X], f:X=>Unit) : Unit = xs match
- case Nil => ()
- case y::ys =>
- f (y)
- foreach (ys, f)
- val xss = List(List(11,21,31),List(),List(41,51))
- foreach (xss, (xs:List[Int]) => println (xs.length))
Using the builtin List class foreach method
- def print (x:Int) = println (x)
- xs.foreach (print)
- xs.foreach ((x:Int) => println (x))
- xs.foreach (x => println (x))
- xs.foreach (println)
- def foreach [X] (xs:List[X], f:X=>Unit) : Unit = ...
X is a type parameter
f is a parameter of function type: (X=>Unit)
XUnitwhile, for)- def length (xs:List[Int]) : Int =
- var length: Int = 0
- var current = xs
- while current != Nil do
- length = length + 1
- current = current.tail
- end while
- length
- end length
- def length [X] (xs: List[X]) : Int = xs match
- case Nil => 0
- case _ :: ys => 1 + length (ys)
- end length
Example: length (List (1, 2, 3))
Imperative iteration
- --> current = 1::(2::(3::Nil)), length = 0
- --> current = 2::(3::Nil), length = 1
- --> current = 3::Nil, length = 2
- --> current = Nil, length = 3
The state of the computation is in mutable variables
Recursive iteration
- --> length (1::(2::(3::Nil)))
- --> 1 + length (2::(3::Nil))
- --> 1 + (1 + length (3::Nil))
- --> 1 + (1 + (1 + length (Nil)))
- --> 1 + (1 + (1 + 0))
- --> 1 + (1 + 1)
- --> 1 + 2
- --> 3
The state of the computation is the expression
- def reference [X] (xs:List[X]) : List[X] = xs
- xs eq reference(xs) /* reference equality: true */
- xs == reference(xs) /* value equality: true */
- def copy [X] (xs:List[X]) : List[X] = xs match
- case Nil => Nil
- case y::ys => y::copy(ys)
- xs eq copy(xs) /* reference equality: false */
- xs == copy(xs) /* value equality: true */
Return a transformed copy of a list
- def map [X,Y] (xs:List[X], f:X=>Y) : List[Y] = xs match
- case Nil => Nil
- case y::ys => f(y) :: map (ys, f)
- val xs = List(11,21,31)
- map(xs, (y:Int) => "x=" + y) // result: List("x=11","x=21","x=31")
copy is just a specialization of map with f:X=>X = x=>xforeach is just a specialization of map with f:X=>Unitxs.map ("x=" + _)List[List[Int]] to a List[Int]: length of inner lists- List(List(11,21,31),List(),List(41,51)).map (_.length)
- // res1: List[Int] = List(3, 0, 2)
List[String] to a List[Int]: length of strings- List("hi", "it's", "me").map (_.length)
- // res1: List[Int] = List(2, 4, 2)
List[Int] to a List[Int]: increment each value- List(1, 2, 3, 4).map (_ + 1)
- // res1: List[Int] = List(2, 3, 4, 5)
Copy only elements that satisfy a predicate f
- def filter [X] (xs:List[X], f:X=>Boolean) : List[X] = xs match
- case Nil => Nil
- case y::ys if f (y) => y :: filter (ys, f)
- case _::ys => filter (ys, f)
- val zs = (0 to 7).toList
- filter(zs, ((_:Int) % 3 != 0))
map- xs.map (x => "x=" + x)
foreach- xs.foreach (x => println ("x=" + x))
yield- for x <- xs yield "x=" + x
do- for x <- xs do println ("x=" + x)
filter- zs.filter (z => z % 3 != 0)
- // shorter: zs.filter(_ % 3 != 0)
- zs.filter (z => z % 3 != 0).
- map (z => "z=" + z)
- for z <- zs
- if z % 3 != 0 yield z
- for z <- zs
- if z % 3 != 0 yield "z=" + z
Multiple iterators
- val xss = List( List(11,21,31), List(), List(41,51) )
- for xs <- xss
- x <- xs
- yield (x, xs.length)
- // res1: List[(Int, Int)] = List((11,3), (21,3), (31,3), (41,2), (51,2))
Cross product of independent iterators
- val xs = List(11,21,31)
- val ys = List("a","b")
- for x <- xs
- y <- ys yield (x, y)
- // res1: List[(Int, String)] = List(
- // (11,a), (11,b),
- // (21,a), (21,b),
- // (31,a), (31,b))
- (for x <- (1 to 7)
- y <- (1 to 9) yield (x, y)
- ).length
- // res1: Int = 63
- val xs = List(11,21,31)
- val ys = List("a","b")
- for x <- xs
- y <- ys yield (x, y)
xs : List[Int]ys : List[String]- x : Int
- y : String
yield provides the type for the result- (x, y) : (Int, String)
- for ... yield (x, y) : List[(Int, String)]
foreach, map, etc. as argumentf do?- def f [X] (xs: List[X]) : List[X] = xs match
- case Nil => Nil
- case y :: ys => f (ys) ::: List (y)
f (Nil)- f (Nil)
- --> Nil
f (3::Nil)- f (3::Nil)
- --> f (Nil) ::: List (3)
- --> Nil ::: List (3)
- --> List (3)
f (2::3::Nil)- f (2::(3::Nil))
- --> f (3::Nil) ::: List (2)
- --> List (3) ::: List (2)
- --> List (3, 2)
f (1::2::3::Nil)- f (1::(2::(3::Nil)))
- --> f (2::(3::Nil)) ::: List (1)
- --> List (3, 2) ::: List (1)
- --> List (3, 2, 1)
f is reverse- def append [X] (xs: List[X], ys: List[X]) : List[X] = xs match
- case Nil => ys
- case z :: zs => z :: append (zs, ys)
- append (1::(2::Nil), 3::Nil)
- --> 1::(append (2::Nil, 3::Nil)) // z = 1, zs = 2::Nil
- --> 1::(2::(append (Nil, 3::Nil))) // z = 2, zs = Nil
- --> 1::(2::(3::Nil)) // z = 2, zs = Nil
1 and 2 created3::Nil is reused (shared)List class has builtin method :::- scala> ((1 to 5).toList) ::: ((10 to 15).toList)
- res1: List[Int] = List(1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15)
Revisit the copy operation
- def copy [X] (xs:List[List[X]]) : List[List[X]] = xs match
- case Nil => Nil
- case y::ys => y :: copy (ys)
- val xss = List(List(11,21,31),List(),List(41,51))
- copy(xss)
- res1: List(List(11,21,31),List(),List(41,51))
Create a copy with a flat structure: replace :: with :::
- def flatten [X] (xs:List[List[X]]) : List[X] = xs match
- case Nil => Nil
- case y::ys => y ::: flatten (ys)
- val xss = List(List(11,21,31),List(),List(41,51))
- flatten(xss)
- res1: List(11,21,31,41,51)
Revisit method map
- def map [X,Y] (xs:List[X], f:X=>List[Y]) : List[List[Y]] = xs match
- case Nil => Nil
- case y::ys => f(y) :: map (ys, f)
- val as = List(3,0,2)
- map (as, (x:Int) => (1 to x).toList)
- res1: List(List(1,2,3), List(), List(1,2))
Create a transformed list with a flat structure: replace :: with :::
- def flatMap [X,Y] (xs:List[X], f:X=>List[Y]) : List[Y] = xs match
- case Nil => Nil
- case y::ys => f(y) ::: flatMap (ys, f)
- val as = List(3,0,2)
- flatMap(as, (x:Int) => (1 to x).toList)
- res1: List(1,2,3,1,2):::Nil
Argument and return types of map and flatMap
map: (List[X],X=>List[Y]) => List[List[Y]]
List[Y]xsflatMap: (List[X],X=>List[Y]) => List[Y]
YList[Y]flatMap- xss.flatMap (x=>x) // same as xss.flatten
- for xs <- xss; x <- xs yield x