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 x::xt => |
| |
| printList (xt) |
| |
| 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 x::xt => |
| println (x) |
| printList (xt) |
| |
| 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 x::xt => |
| println ("0x%02x".format(x)) |
| printListFormat (xt) |
| |
| 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 x::xt => |
| println (x.length) |
| printListLengths (xt) |
| |
| 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 x::xt => |
| f (x) |
| foreach (xt, 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 x::xt => |
| f (x) |
| foreach (xt, 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 x::xt => |
| f (x) |
| foreach (xt, 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 x::xt => |
| f (x) |
| foreach (xt, 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 x::xt => |
| f (x) |
| foreach (xt, 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) |
| xs == reference(xs) |
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 x::xt => x::copy(xt) |
| |
| xs eq copy(xs) |
| xs == copy(xs) |
res1: Boolean = false
res2: Boolean = true
Transformer
Return a transformed copy
| def transform (xs:List[Int]) : List[String] = xs match |
| case Nil => Nil |
| case x::xt => ("0x%02x".format (x)) :: transform (xt) |
| |
| 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 x::xt => (x.length) :: transform (xt) |
| |
| 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 x::xt => ("0x%02x".format (x)) :: transform (xt) |
| |
| 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 x::xt => f(x) :: map (xt, 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 x::xt => f(x) :: map (xt, 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 x::xt => f(x) :: map (xt, f) |
| |
| val xs = List(11,21,31) |
| map(xs, (x:Int) => "0x%02x".format (x)) |
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 x::xt => x :: copy (xt) |
| |
| 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 x::xt => f(x) :: map (xt, f) |
| |
| val xs = List(11,21,31) |
| map(xs, (x:Int) => "0x%02x".format (x)) |
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 x::xt =>{f(x) ; foe (xt, f) } |
| |
| val xs = List(11,21,31) |
| foe(xs, (x:Int) => println ("0x%02x".format(x))) |
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 x::xt => x :: copy (xt) |
| |
| |
| 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 x::xt if f (x) => x :: filter (xt, f) |
| case _::xt => filter (xt, 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 x::xt => x :: copy (xt) |
| |
| 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 x::xt => x ::: flatten (xt) |
| |
| 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 x::xt => f(x) :: map (xt, 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 x::xt => f(x) ::: flatMap (xt, 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)
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)]
CSC447 Concepts of Programming Languages Functions over Lists Instructor: James Riely