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 (s"y=$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 printY (y:Int) = println(s"y=$y")
- val xs = List(11,21,31)
- foreach (xs, printY)
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
foreach visits every element
- def foreach [X] (xs:List[X],
- f:X=>Unit) : Unit =
- xs match
- case Nil => ()
- case y::ys => f(y); foreach(ys, f)
- end foreach
Unit: no result collectedmap: visits and transforms every element
- 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)
- end map
List[Y]: transformed copy- map(List(11,21,31), (y:Int) => "x=" + y)
- // res: List[String] = List("x=11","x=21","x=31")
xs.map((y:Int) => "x=" + y)List[Int]=>List[Int]- List(1, 2, 3, 4).map(x => x + 1)
- // res: List[Int] = List(2, 3, 4, 5)
List[List[Int]]=>List[Int]- List(List(11,21,31),Nil,List(41,51)).map(xs => xs.length)
- // res: List[Int] = List(3, 0, 2)
List[String]=>List[Int]- List("hi", "it's", "me").map(xs => xs.length)
- // res: List[Int] = List(2, 4, 2)
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)
- end filter
- val zs = (0 to 7).toList
- filter(zs, (x => x % 3 != 0))
- // res: List[Int] = List(1,2,4,5,7)
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)
- 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)]
Higher-order functions
foreach: visits every elementmap: creates transformed copyfilter: creates filtered copyList comprehensions
for ... do mimics foreachfor ... yield mimics mapfor ... if ... yield mimics filter (+ map)f 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
# <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Reference and Structural Equality - Return a reference to a list ```scala def reference [X] (xs:List[X]) : List[X] = xs xs eq reference(xs) /* reference equality: true */ xs == reference(xs) /* value equality: true */ ``` --- # <span class="fa-stack"><i class="fa-solid fa-circle fa-stack-2x"></i><i class="fa-solid fa-book fa-stack-1x fa-inverse"></i></span> Reference and Structural Equality - Return a copy of a list ```scala 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 */ ``` ---
* every element in input is transformed into a modified element in output
* :fa fa-lightbulb: `copy` is a specialization of `map` with `f:X=>X = x=>x` * :fa fa-lightbulb: `foreach` is a specialization of `map` with `f:X=>Unit`