Expressions, Values, Types
Functions
Collections
Elegant way to systematically
Generate Combinations
Search Combinations
Generate & Search Combinations
val res = for (i <- 1 to 10;
j <- 1 to 10;
k <- 1 to 10
if i*i + j*j == k*k)
yield (i, j, k)
What is the value of res ?
A. Vector()
B. Vector((3,4,5), (6,8,10))
C. Vector((6,8,10), (3,4,5))
D. Vector((3,4,5), (4,3,5), (6,8,10), (8,6,10))
E. Vector((8,6,10), (6,8,10), (4,3,5), (3,4,5))
val res = for (i <- 1 to 10;
j <- 1 to 10;
k <- 1 to 10
if i*i + j*j == k*k)
yield (i, j, k)
Is equivalent to:
val res = (for (i <- 1 to 10;j <- 1 to 10; k <- 1 to 10)
yield (i, j, k)
).filter((i, j, k) => i*i + j*j == k*k)
which is equivalent to:
Vector( (1,1,1), (1,1,2), ..., (1,1,10), (1,2,1),..., (1,10,10)
, (2,1,1), (2,1,2), ..., (2,1,10), (2,2,1),..., (2,10,10)
, ...,
,(10,1,1), (10,1,2),...,(10,1,10), (10,2,1),...,(10,10,10)
).filter((i, j, k) => i*i + j*j == k*k)
filter Preserves Sequence OrderHence, the expression
Vector( (1,1,1), (1,1,2), ..., (1,1,10), (1,2,1),..., (1,10,10)
, (2,1,1), (2,1,2), ..., (2,1,10), (2,2,1),..., (2,10,10)
, ...,
,(10,1,1), (10,1,2),...,(10,1,10), (10,2,1),...,(10,10,10)
).filter((i, j, k) => i*i + j*j == k*k)
evaluates to ...
D. Vector((3,4,5), (4,3,5), (6,8,10), (8,6,10))
- How would you avoid duplicates?
case class Name(x1: T1, x2: T2, ..., xn: TN)
Creates a class Name with
- Immutable fields
x1of typeT1...xnof typeTn
- Constructor method for object creation
- Extractor method for pattern matching (later...)
NovelHere is a case class (just fancy records for now...)
case class Novel(title: String, author: String)
You can create instances thus (no need for new)
scala> val n1 = Novel ("A Feast For Crows", "George R. R. Martin")
n1: Novel = Novel(A Feast For Crows,George R. R. Martin)
and access fields like so
scala> n1.author
n1: String = George R. R. Martin
NovelHere is another case class (just fancy records for now...)
case class Album(title: String, artist: String)
You can create instances thus (no need for new)
scala> val a = Album("We are glitter", "Goldfrapp")
a: Album = Album(We are glitter,Goldfrapp)
Suppose we have a "database" of novels
val novels =
List( Novel ( "Skinny Legs and All"
, "Tom Robbins")
, Novel ( "Breakfast of Champions"
, "Kurt Vonnegut")
, Novel ( "An Object of Beauty"
, "Steve Martin")
, Novel ( "Sophie's Choice"
, "William Styron")
, Novel ( "The Other"
, "Tom Tryon")
)
... and a "database" of albums
val albums =
List( Album ( "Rare Bird Alert"
, "Steve Martin")
, Album ( "Paper Airplane"
, "Alison Krauss and Union Station")
, Album ( "Earl Scruggs and Friends"
, "Earl Scruggs")
)
... and a "database" of movies
val movies =
List( Movie ("Airplane",
List ( "Leslie Nielsen", "Robert Stack"))
, Movie ("Young Frankenstein",
List ("Gene Wilder", "Madeline Kahn"))
, Movie ("The Pink Panther",
List ("Steve Martin", "Jean Reno"))
)
tripleThreat ?A person who has
- written a book,
- acted in a movie,
- performed in an album ?
tripleThreat ?A person who has
Cue, nested loop query...
for (n <- novels;
a <- albums;
m <- movies;
if (n.author == a.artist && m.actors.contains (n.author)))
yield (n.author)
tripleThreat ?A person who has
Cue, nested loop query...
for (n <- novels;
a <- albums;
m <- movies;
if (n.author == a.artist && m.actors.contains (n.author)))
yield (n.author)
Which evaluates to
tripleThreats: List[String] = List(Steve Martin)
- Not necessarily very efficient of course ...
You can define a special collection type such that the expression
for (n <- novels;
a <- albums;
m <- movies;
if (n.author == a.artist && m.actors.contains (n.author)))
yield (n.author)
is automatically converted into an SQL (DataBase) query ...!
- Not on final...
Elegant way to systematically
Generate Combinations
Search Combinations
Generate & Search Combinations
Given a chess board of size n rows and n columns
Find a placement of n queens so none can kill each other!

Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!

The first question: what is a solution ?
The first question: what is a solution ?
We represent each queen by its position XY: a pair of (row, col)
type XY = (Int, Int)
The first question: what is a solution ?
We represent each queen by its position XY: a pair of (row, col)
type XY = (Int, Int)
One queen q1 can kill another q2 if
def canKill(q1: XY, q2: XY) =
q1._1 == q2._1 || // same row
q1._2 == q2._2 || // same column
(q1._1 - q2._1).abs == (q1._2 - q2._2).abs // same diagonal
canKill(q1, q2) is true if a queen at q1 can kill another q2
queens : List[XY] is a list of occupied positions
q : XY is a new queen position.
Which expression evaluates to true exactly when none of queens can kill q ?
A. for (qo <- queens) yield !canKill(qo, q)
B. for (qo <- queens) yield canKill(qo, q)
C. queens.exists(!canKill(_,q))
D. queens.forall(!canKill(_,q))
E. queens.map(!canKill(_,q)).foldLeft(true)(_ && _)
List(q1,q2,q3).map(!canKill(_, q))
====> List(!canKill(q1, q), !canKill(q2,q), !canKill(q3,q))
====> List(ck1, ck2, ck3).foldLeft(true)(_ && _)
====> ck1 && ck2 && ck3
canKill(q1, q2) is true if a queen at q1 can kill another q2
queens : List[XY] is a list of occupied positions
q : XY is a new queen position.
- When for each
qoinqueens,qocannot killq
queens.forall (qo => !(canKill(qo, q))
- D:
queens.forall (!(canKill(_, q))
- However, E: is also right. (Why?)
The first question: what is a solution ?
We represent each queen by its position XY: a pair of (row, col)
type XY = (Int, Int)
One queen q1 can kill another q2 if
def canKill(q1: XY, q2: XY) =
eqRow(q1, q2) || eqCol(q1, q2) || eqDia(q1, q2)
A queen at q is safe w.r.t. other queens if:
def isSafe(queen: XY, others: List[XY]) =
others forall (!canKill(_, queen))
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
def placeQueens(columns: Int, row: Int) : List[XY]
placeQueens(n,n) ===> val others = placeQueens(n, n-1) for (x <- 0..n, y <- 0..n) if (isSafe((x,y), others) yield (x,y) :: others
placeQueens(N, r) =
To place queens on row r ... 0
- (recursively) place queens on rows
r-1...0
- for each column in row
r
- return position which
isSafew.r.t. recursive solution
- Can anybody see a problem?
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
- Can be many ways of placing rows
r-1...0
- ... but only a few may be compatible with row
r!
- Return all ways of placing rows
r-1...0
- Try each placement for row
r!
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
def placeQueens(columns: Int, row: Int) : List[List[XY]]
To place queens on row r ... 0
- (recursively) place queens on rows
r-1...0
- for each (recursive) solution
- for each column in row
r
yieldposition whichisSafew.r.t. recursive solution
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
def placeQueens(columns: Int, row: Int) : List[List[XY]] =
if (row == 0)
List(List())
else
for { queens <- placeQueens(columns, row - 1)
column <- 1 to columns
queen = (row, column)
if isSafe(queen, queens) }
yield (queen :: queens)
- Note: the base case!
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
Just printing the 1 solution (of several)
scala> Queens(4, 1)
OOXO
XOOO
OOOX
OXOO
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
Just printing the 1 solution (there are many)
scala> Queens(8, 1)
OOOXOOOO
OXOOOOOO
OOOOOOXO
OOXOOOOO
OOOOOXOO
OOOOOOOX
OOOOXOOO
XOOOOOOO
Given a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
scala> Queens(2, 1)
scala> Queens(3, 1)
scala>
- Say what?! There are no solutions for \(N = 2, 3\)
List[List[Int]]
5, 3, , , 7, , , ,
6, , , 1, 0, 5, , ,
, 0, 8, , , , , 6,
8, , , , 6, , , , 3
4, , , 8, , 3, , , 1
7, , , , 2, , , , 6
, 6, , , , , 2, 8,
, , , 4, 1, 0, , , 5
, , , , 8, , , 7, 0
List(List(5, 3, 4, 6, 7, 8, 0, 1, 2)
,List(6, 7, 2, 1, 0, 5, 3, 4, 8)
,List(1, 0, 8, 3, 4, 2, 5, 6, 7)
,List(8, 5, 0, 7, 6, 1, 4, 2, 3)
,List(4, 2, 6, 8, 5, 3, 7, 0, 1)
,List(7, 1, 3, 0, 2, 4, 8, 5, 6)
,List(0, 6, 1, 5, 3, 7, 2, 8, 4)
,List(2, 8, 7, 4, 1, 0, 6, 3, 5)
,List(3, 4, 5, 2, 8, 6, 1, 7, 0))
The first question: what is a sudoku board ?
The first question: what is a sudoku board ?
We represent each square by its position XY: a pair of (row, col)
type XY = (Int, Int)
The first question: what is a sudoku board ?
We represent each square by its position XY: a pair of (row, col)
type XY = (Int, Int)
We represent the board by a (partial) map from XY to Int
type Board = Map[XY, Int]
- How is a blank (unfilled) square represented?
- Simple: the position
XYis just not in the map!
5, 3, , , 7, , , ,
6, , , 1, 0, 5, , ,
, 0, 8, , , , , 6,
8, , , , 6, , , , 3
4, , , 8, , 3, , , 1
7, , , , 2, , , , 6
, 6, , , , , 2, 8,
, , , 4, 1, 0, , , 5
, , , , 8, , , 7, 0
is represented by
Map ((0, 0) -> 5, (0, 1) -> 3, (0, 4) -> 7,
(1, 0) -> 6, (1, 3) -> 1, (1, 4) -> 0, (1, 5) -> 5,
(2, 1) -> 0, (2, 2) -> 8, (2, 7) -> 6,
(3, 0) -> 8, (3, 4) -> 6, (3, 8) -> 3,
(4, 0) -> 4, (4, 3) -> 8, (4, 5) -> 3, (4, 8) -> 1,
(5, 0) -> 7, (5, 4) -> 2, (5, 8) -> 6,
(6, 1) -> 6, (6, 6) -> 2, (6, 7) -> 8,
(7, 3) -> 4, (7, 4) -> 1, (7, 5) -> 0, (7, 8) -> 5,
(8, 4) -> 8, (8, 7) -> 7, (8, 8) -> 0)
Given a partially filled Sudoku Board
Find a way to fill in all squares
- If no blank squares on board then
- return board!
- Else pick blank with fewest possible choices
- for each choice
- recursively solve board with blank filled by choice
Given a partially filled Sudoku Board
Find a way to fill in all squares
def solve(b: Board) : Iterator[Board] = {
val blanks = candidates(b)
if (blanks.isEmpty) Iterator(b)
else {
val (xy, choices) = blanks minBy (_._2.length)
for (choice <- choices.toIterator;
bFull <- solve(b + (xy -> choice)))
yield bFull
}
}
def solve(b: Board) : Iterator[Board] = {
val blanks = candidates(b)
if (blanks.isEmpty) Iterator(b)
else {
val (xy, choices) = blanks minBy (_._2.length)
for (choice <- choices.toIterator;
bFull <- solve(b + (xy -> choice)))
yield bFull
}
}
- Why do we compute all solutions?
- Why the
.toIteratorcall?
candidates for a boarddef candidates (b: Board) =
for { x <- 0 until 9 ;
y <- 0 until 9 ;
if (!b.isDefinedAt((x,y))) ;
xy = (x, y) ;
vals = (0 until 9) diff (alreadyTaken(x,y,b)) }
yield (xy, vals)
- Loop over all squares,
- Filter the ones that are not defined,
- Prune the already taken values for the square,
- Yield a tuple of the cell and remaining values.
alreadyTaken values for a squaredef alreadyTaken(x: Int, y: Int, b: Board) =
for (p <- pointsOfCol(x) ++
pointsOfRow(y) ++
pointsOfGrid(x, y)
if b.isDefinedAt(p))
yield b(p)
- Loop over squares in same row, col, grid
- Yield values at the square (if defined)
def pointsOfGrid(x: Int, y: Int) = {
val (x0, y0) = ((x/3) * 3, (y/3) * 3)
for (px <- x0 until x0 + 3 ;
py <- y0 until y0 + 3 )
yield (px, py)
}
~~~{.scala} def pointsOfCol(x: Int) = for (y <- 0 until 9) yield (x, y)
def pointsOfRow(y: Int) = for (x <- 0 until 9) yield (x, y) ~~~{.scala}
Elegant way to systematically
Generate Combinations
Search Combinations
Generate & Search Combinations
- Recursively generate & solve sub-problems, and
- Loop over candidates & yield valid solutions
Iterator vs. ListIterator vs. ListWe chose Iterator and not List for password crack
words.toList.filter(...).toIterator // yuck
Iterator vs. ListWe chose Iterator and not List for password crack
words.toList.filter(...).toIterator // yuck
words.filter(...) // sweet!
- The yucky version is not just longer ...
- ... it can be massively inefficient!
Iterator vs. ListWe chose Iterator and not List for password crack
Iterator lazily computes one element at a timeAt each .next call
- Does not build the entire sequence up at once! (as
Listdoes)
- Does not store the entire sequence in memory! (as
Listdoes)
Iterator vs. ListWe chose Iterator and not List for password crack
Iterator lazily computes one element at a timeAt each .next call
Does not build the entire sequence up at once! (as List does)
Does not store the entire sequence in memory! (as List does)
Iterator good for operating on big data streams
- Like large dictionaries of words ...
- ... and transformations thereof
Iterator vs. ListIterator is lazy, good for operating on big data streamsdef allVowels(megaDictionary: Iterator[String]): Iterator[String] =
for { w <- megaDictionary
; if hasAllVowels(w) }
yield w
Iteratoruses O(1) memory (at any given time) ...
def allVowels(megaDictionary: List[String]): List[String] =
for (w <- megaDictionary
if hasAllVowels(w))
yield w
Listuses O(N) memory, where N =megaDictionarysize.
Iterator vs. ListWe chose Iterator and not List for password crack
Iterator is lazy, good for operating on big data streamsdef txReverse(megaDictionary: Iterator[String]): Iterator[String] =
for (w <- megaDictionary
c <- Iterator(w, reverse(w))
yield c
Iterator uses O(1) memory (at any given time) ...def txReverse(megaDictionary: List[String]): List[String] =
for (w <- megaDictionary
c <- Iterator(w, reverse(w))
yield c
List uses O(N) memory, where N = megaDictionary size.Iterator vs. ListWe chose Iterator and not List for password crack
Iterator is lazy, good for operating on big data streamsIterator ?
- Cannot reuse after reaching the end
- Cannot rewind
Iterator
- Cannot randomly access (efficiently)
For PA5
- Stick to using
forandyieldand HOFS
- Do not convert from
IteratortoList
- Or you will have all sorts of (unsolvable) memory issues