CSC447

Concepts of Programming Languages

Functions over Lists

Instructor: James Riely

Print Every Element

How to print every element of a list?

def printList (xs:List[Int]) : Unit = 







val xs = List(11,21,31)
printList (xs)
          
  
  
          

Print Every Element

How to print every element 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 Every Element

How to print every element of a list?

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)
          

11
21
31
          

Format Every Element

Do something different?

def printListFormat (xs:List[Int]) : Unit = xs match 
  case Nil   => ()
  case y::ys => 
    println ("0x%02x".format(y))
    printListFormat (ys)

val xs = List(11,21,31)
printListFormat (xs)
          

0x0b
0x15
0x1f
          

Format Every Element

Different type of list?

def printListLengths (xs:List[List[Int]]) : Unit = xs match 
  case Nil   => ()
  case y::ys => 
    println (y.length)
    printListLengths (ys)

val xss = List(List(11,21,31),List(),List(41,51))
printListLengths (xss)
          

3
0
2
          

Foreach

Abstract 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)
            

11
21
31
            

Foreach

Easy to make variations

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)
            

0x0b
0x15
0x1f
            

Foreach

Abstract the type

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)
          

3
0
2
          

Foreach

Anonymous function, AKA lambda expression

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))
          

3
0
2
          

Foreach

Anonymous function, AKA lambda expression

foreach (xss, (xs:List[Int]) => println (xs.length))
          
Versus

def printLength (xs:List[Int]) = println (xs.length)
foreach (xss, printLength)
          
Also possible

val printLength = (xs:List[Int]) => println (xs.length)
foreach (xss, printLength)
            

Type and function parameters


def foreach [X] (xs:List[X], f:X=>Unit) : Unit = ...
          

foreach: [X](xs: List[X], f: X => Unit)Unit
          
  • X is a type parameter
    • Type parameters in square brackets
    • Value parameters in round brackets
    • Types before values
  • f is a parameter of function type: (X=>Unit)
    • takes an argument of type X
    • returns a result of type Unit

Builtin foreach

Scala List class has foreach method

xs.foreach ((x:Int) => println ("0x%02x".format(x)))
          

0x0b
0x15
0x1f
          
vs our own foreach function

foreach (xs, ((x:Int) => println ("0x%02x".format(x)))
            

0x0b
0x15
0x1f
            

Type inference

Types unnecessary if Scala can infer

xs.foreach (x => println ("0x%02x".format(x)))
          

0x0b
0x15
0x1f
          

Builtin foreach

Special syntax

for x <- xs do println ("0x%02x".format(x))
          
is compiled to

xs.foreach (x => println ("0x%02x".format(x)))
            
for loops use foreach and more...

Anonymous functions

Using lambda notation

val add = (x:Int, y:Int) => x+y
add(11,21)
          
Use underscore when parameters used exactly once

val add = (_:Int) + (_:Int)
add(11,21)
            
Types may be inferred in some contexts

var add : (Int,Int)=>Int = null  
add = (x,y) => x+y
add = _ + _
add(11,21)
            

Imperative loops

Using foreach and a variable in scope

def sum (xs:List[Int]) : Int = 
  var result = 0
  xs.foreach ((x:Int) => result = result + x)
  result
          
(but folds are better! stay tuned...)

Debugging recursive functions

Use a compound expression

def foreach [X] (xs:List[X], f:X=>Unit) : Unit = 
  println("call foreach(%s)".format(xs))
  xs match 
    case Nil   => ()
    case y::ys => 
      f (y)
      foreach (ys, f)
  println("rtrn foreach(%s)".format(xs))

val xs = List(21,31)
foreach (xs, (x:Int) => ())
          

call foreach(List(21, 31))
call foreach(List(31))
call foreach(List())
rtrn foreach(List())
rtrn foreach(List(31))
rtrn foreach(List(21, 31))
          

Equality

Return a reference to a list

def reference (xs:List[Int]) : List[Int] = xs



xs eq reference(xs) /* reference equality */
xs == reference(xs) /* value equality */
          
res1: Boolean = true
res2: Boolean = true


          

Equality

Return a copy of a list

def copy (xs:List[Int]) : List[Int] = xs match 
  case Nil   => Nil
  case y::ys => y::copy(ys)

xs eq copy(xs) /* reference equality */
xs == copy(xs) /* value equality */
          
res1: Boolean = false
res2: Boolean = true


          

Transformer

Return a transformed copy

def transform (xs:List[Int]) : List[String] = xs match 
  case Nil   => Nil
  case y::ys => ("0x%02x".format (y)) :: transform (ys)

val xs = List(11,21,31)
transform(xs)
          
res1: List[String] = List(0x0b, 0x15, 0x1f)



          

Transformer

Return a transformed copy

def transform (xs:List[List[Int]]) : List[Int] = xs match 
  case Nil   => Nil
  case y::ys => (y.length) :: transform (ys)

val xss = List(List(11,21,31),List(),List(41,51))
transform(xss)
          
res1: List[Int] = List(3, 0, 2)



          

Transformer

One cons cell in input => one cons cell in output

def transform (xs:List[Int]) : List[String] = xs match 
  case Nil   => Nil
  case y::ys => ("0x%02x".format (y)) :: transform (ys)

val xs = List(11,21,31)
transform(xs)
          

transform (11::(21::(31::Nil)))
--> 0x0b::(transform (21::(31::Nil)))
--> 0x0b::(0x15::(transform (31::Nil)))
--> 0x0b::(0x15::(0x1f::(transform (Nil))))
--> 0x0b::(0x15::(0x1f::(Nil)))
          

Map

Abstracting f

def map (xs:List[Int], f:Int=>String) : List[String] = xs match 
  case Nil   => Nil
  case y::ys => f(y) :: map (ys, f)

val xs = List(11,21,31)
map(xs, "0x%02x".format (_))
          

map (11::(21::(31::Nil)), f)
--> 0x0b::(map (21::(31::Nil), f))
--> 0x0b::(0x15::(map (31::Nil, f)))
--> 0x0b::(0x15::(0x1f::(map (Nil, f))))
--> 0x0b::(0x15::(0x1f::(Nil)))
          

Map

Showing f

def map (xs:List[Int], f:Int=>String) : List[String] = xs match 
  case Nil   => Nil
  case y::ys => f(y) :: map (ys, f)

val xs = List(11,21,31)
map(xs, "0x%02x".format (_))
          

map (11::(21::(31::Nil)), f)
--> f(11)::(map (21::(31::Nil), f))
--> f(11)::(f(21)::(map (31::Nil, f)))
--> f(11)::(f(21)::(f(31)::(map (Nil, f))))
--> f(11)::(f(21)::(f(31)::(Nil)))
          

Map

Abstracting X and Y

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) => "0x%02x".format (y))
          

map (11::(21::(31::Nil)), f)
--> f(11)::(map (21::(31::Nil), f))
--> f(11)::(f(21)::(map (31::Nil, f)))
--> f(11)::(f(21)::(f(31)::(map (Nil, f))))
--> f(11)::(f(21)::(f(31)::(Nil)))
          

Copy

Recall copy

def copy [X] (xs:List[X]) : List[X] = xs match 
  case Nil   => Nil
  case y::ys => y :: copy (ys)

val xs = List(11,21,31)
copy(xs)
          

copy (11::(21::(31::Nil)))
--> 11::(copy (21::(31::Nil)))
--> 11::(21::(copy (31::Nil)))
--> 11::(21::(31::(copy (Nil))))
--> 11::(21::(31::(Nil)))
          

Map

copy(xs) same as map (xs, x=>x)

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) => "0x%02x".format (y))
          

map (11::(21::(31::Nil)), f)
--> f(11)::(map (21::(31::Nil), f))
--> f(11)::(f(21)::(map (31::Nil, f)))
--> f(11)::(f(21)::(f(31)::(map (Nil, f))))
--> f(11)::(f(21)::(f(31)::(Nil)))
          

Foreach

Recall foreach

def foe [X]   (xs:List[X], f:X=>Unit) : Unit = xs match 
  case Nil   => ()
  case y::ys =>{f(y) ;  foe (ys, f) }

val xs = List(11,21,31)
foe(xs, (y:Int) => println ("0x%02x".format(y)))
          

foe (11::(21::(31::Nil)))
--> foe (21::(31::Nil))
--> foe (31::Nil)
--> foe (Nil)
--> ()
          

Builtin map

Map method

xs.map ("0x%02x".format (_))
          

res1: List[String] = List(0x0b, 0x15, 0x1f)
          
Same as our map function

map (xs, "0x%02x".format (_))
            

res1: List[String] = List(0x0b, 0x15, 0x1f)
            

Builtin map

For expression

for x <- xs yield "0x%02x".format(x)
          

for x <- xs do println ("0x%02x".format(x))
          
is compiled to

xs.map (x => "0x%02x".format(x))
            

xs.foreach (x => println ("0x%02x".format(x)))
            
Difference is the yield

More map examples


val xss = List(List(11,21,31),List(),List(41,51))
xss.map (_.length)
            

res1: List[Int] = List(3, 0, 2)
            

val ys = List("hi", "mom", "it's", "me")
ys.map (_.length)
            

res1: List[Int] = List(2, 3, 4, 2)
            

Copy

Recall copy

def copy   [X] (xs:List[X])               : List[X] = xs match 
  case Nil            => Nil
  case y::ys          => y :: copy   (ys)


val zs = (0 to 7).toList
copy(zs)
          

zs: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7)
res1: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7)
          

Filter

Only copy elements satisfying 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))
          

zs: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7)
res1: List[Int] = List(1, 2, 4, 5, 7)
          

Builtin filter


for z <- zs; if z % 3 != 0 yield z
 zs.filter (z => z % 3 != 0)
          

for z <- zs; if z % 3 != 0 yield "0x%02x".format(z)
 zs.filter (z => z % 3 != 0).map (z => "0x%02x".format(z))
          

for z <- zs; if z % 3 != 0 do println ("0x%02x".format(z))
 zs.filter (z => z % 3 != 0).foreach (z => println ("0x%02x".format(z)))
          

Copy

Recall copy

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   (List(11,21,31) ::  List() ::  List(41,51) ::  Nil)
--> List(11,21,31) ::  copy   (List() ::  List(41,51) ::  Nil)
--> List(11,21,31) ::  List() ::  copy   (List(41,51) ::  Nil)
--> List(11,21,31) ::  List() ::  List(41,51) ::  copy   (Nil)
--> List(11,21,31) ::  List() ::  List(41,51) ::  Nil
=== List(List(11,21,31), List(), List(41,51))
          

Flatten

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(List(11,21,31) ::  List() ::  List(41,51) ::  Nil)
--> List(11,21,31) ::: flatten(List() ::  List(41,51) ::  Nil)
--> List(11,21,31) ::: List() ::: flatten(List(41,51) ::  Nil)
--> List(11,21,31) ::: List() ::: List(41,51) ::: flatten(Nil)
--> List(11,21,31) ::: List() ::: List(41,51) ::: Nil
=== List(11,21,31,41,51)
          

Map

Recall 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)
          

map     (3::0::2::Nil, f)
--> List(1,2,3):: map     (0::2::Nil, f)
--> List(1,2,3):: List():: map     (2::Nil, f)
--> List(1,2,3):: List():: List(1,2):: map     (Nil, f)
--> List(1,2,3):: List():: List(1,2):: Nil
=== List(List(1,2,3), List(), List(1,2))
          

FlatMap

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)
          

flatMap (3::0::2::Nil, f)
--> List(1,2,3):::flatMap (0::2::Nil, f)
--> List(1,2,3):::List():::flatMap (2::Nil, f)
--> List(1,2,3):::List():::List(1,2):::flatMap (Nil, f)
--> List(1,2,3):::List():::List(1,2):::Nil
=== List(1,2,3,1,2):::Nil
          

FlatMap

  • Map f:X=>List[Y]
    • Each element of result list is a List[Y]
    • Length of result = length of xs
  • FlatMap f:X=>List[Y]
    • Each element of result list is a Y
    • Length of result = sum of lengths of each List[Y]

Builtin flatMap


for xs <- xss; x <- xs yield x
          
is compiled to

xss.flatMap (x=>x)
          
which is the same as

xss.flatten
          

Multiple iterators

For expressions are quite general

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))
            
Length is the product

(for x <- (1 to 7); y <- (1 to 9) yield (x, y)).length
            

res1: Int = 63
            

Types in for expressions


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]
res1: List[(Int, String)] = List((11,a), (11,b), (21,a), (21,b), (31,a), (31,b))
          
Scala infers types for iterator variables

x : Int
y : String
            
yield provides the type for the for expression

(x, y) : (Int, String)
for ... yield (x, y) : List[(Int, String)]