Elegant way to systematically:
Elegant way to systematically:
Generate Combinations
Search Combinations
Generate & Search Combinations
for
-loopsval res1 = for (xs <- Array("cat", "dog", "mouse");
x <- xs)
yield x
What is the type of res1
?
for
-loopsval res2 = for (xs <- List("cat", "dog", "mouse");
x <- xs)
yield x
What is the type of res2
?
for
-loopsval res3 = for (kv <- Map("Aa" -> 1, "Bb" -> 2);
c <- kv._1)
yield (c, kv._2)
What is the type of res3
?
A. List[Char, Int]
B. List[(Char, Int)]
C. Map[Char, Int]
D. Map[(Char, Int)]
for
-loopsval res3 = for (kv <- Map("Aa" -> 1, "Bb" -> 2);
c <- kv._1)
yield (c, kv._2)
What is the type of res3
?
yield
ed value.Map[Char, Int]
Map('A'->1, 'a'->1, 'B'->2, 'b'->2)
for
-loopsval res = for ( i <- List(1, 2)
; j <- List("a", "b")) yield (i, j)
What is the value of res
?
A. Nothing (Type Error)
B. List(1, 2, "a", "b")
C. List(1, "a", 2, "b")
D. List((1, "a"), (2, "b"))
E. List((1,"a"), (1,"b"), (2,"a"), (2,"b"))
val res = for ( i <- List(1, 2)
; j <- List("a", "b")) yield (i, j)
i <- 1
j <- a
yield (1, "a")
j <- b
yield (1, "b")
i <- 2
j <- a
yield (2, "a")
j <- b
yield (2, "b")
List((1,"a"), (1,"b"), (2,"a"), (2,"b"))
def bitStrings(n: Int) : List[String] =
if (n <= 0) { List() }
else {
for (c <- List("0", "1");
s <- bitStrings(n-1))
yield(c + s)
}
What does bitStrings(2)
return?
A. Nothing (Type Error)
B. List()
C. List("0", "1")
D. List("00", "01", "10", "11")
def bitStrings(n: Int) : List[String] =
if (n <= 0) { List() } else {
for (c <- List("0", "1");
s <- bitStrings(n-1))
yield(c + s)
}
What does bitStrings(2)
evaluate to? Let's work backwards.
bitStrings(0)
==> List()
bitStrings(1)
==> ?c <- "0"
s <- ...
Nothing! (because bitStrings(0)
==> List()
)c <- "1"
s <- ...
Nothing! (because bitStrings(0)
==> List()
)def bitStrings(n: Int) : List[String] =
if (n <= 0) { List() } else {
for (c <- List("0", "1");
s <- bitStrings(n-1))
yield(c + s)
}
What does bitStrings(2)
evaluate to? Let's work backwards.
bitStrings(0)
==> List()
bitStrings(1)
==> List()
bitStrings(2)
, bitStrings(3)
, etc.Nested loops are like cartesian products:
scala> for ( i <- List(1, 2)
; j <- List("a", "b"))
yield (i, j)
res0: List[(Int, String)] =
List((1,a), (1,b), (2,a), (2,b))
Result is empty if either the first sequence is empty
scala> for ( i <- List()
; j <- List("a", "b"))
yield (i, j)
res2: List[(Nothing, String)] = List()
or the second sequence is empty
scala> for ( i <- List(1,2)
; j <- List())
yield (i, j)
res1: List[(Int, Nothing)] = List()
def bitStrings(n: Int) : List[String] =
if (n <= 0) { List() } else {
for (c <- List("0", "1");
s <- bitStrings(n-1))
yield(c + s)
}
How can we fix this function so that:
bitStrs(1)
==> List("0","1")
bitStrs(2)
==> List("00","01","10","11")
bitStrs(3)
List("000","001","010","011",
"100","101","110","111")
bitStrings(0)
Only bit-string of length 0
is the empty string ""
def bitStrings(n: Int) : List[String] =
if (n <= 0) { List("") } else {
for (c <- List("0", "1");
s <- bitStrings(n-1))
yield(c + s)
}
foo("go")
?def foo(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- foo(w.substring(1)))
yield(c + s)
Hint: "sunday".substring(0, 1)
==> "s"
and
"sun".substring(1)
==> "unday"
A. Nothing (Infinite Loop)
B. List()
C. List("", "o", "g", "go")
D. List("")
E. List("o", "g", "go")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Again, let's work backwards.
subStrings("")
==> List("")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Let's work backwards. subStrings("o")
==> ?
c <- ""
s <- ""
(from subStrings("")
==> List("")
)yield ("" + "")
c <- "o"
s <- ""
(from subStrings("")
==> List("")
)yield ("o" + "")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Let's work backwards. subStrings("o")
==> List("", "o")
c <- ""
s <- ""
(from subStrings("")
==> List("")
) yield ("" + "")
c <- "o"
s <- ""
(from subStrings("")
==> List("")
) yield ("o" + "")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Let's work backwards. subStrings("go")
==> ?
c <- ""
s <- ""
(because subStrings("o")
==> List("", "o")
)yield ("" + "")
s <- "o"
yield ("" + "o")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Let's work backwards. subStrings("go")
==> List("", "o", ???)
c <- "g"
s <- ""
(because subStrings("o")
==> List("", "o")
)yield ("g" + "")
s <- "o"
yield ("g" + "o")
def subStrings(w: String) : List[String] =
if (w == "") { List(w) } else
for (c <- List("", w.substring(0, 1));
s <- subStrings(w.substring(1)))
yield(c + s)
Let's work backwards.
subStrings("go")
==> List("", "o", "g", "go")
substrings
is almost identical to transformCapitalize
subStrings
Replace each character "c"
by either "c"
or ""
transformCapitalize
Replace each character "c"
by either "c"
or "C"
transformDigits
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({ case (i,j,k) => i*i + j*j == k*k })
val res =
(for (i <- 1 to 10; j <- 1 to 10; k <- 1 to 10)
yield (i, j, k)
).filter({ case (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,2,2), ..., ( 1,10,10),
( 2,1,1), ( 2,1,2), ..., ( 2, 1,10),
( 2,2,1), ( 2,2,2), ..., ( 2,10,10),
...,
(10,1,1), (10,1,2), ..., (10, 1,10),
(10,2,1), (10,2,2), ..., (10,10,10)
).filter({ case (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,2,2), ..., ( 1,10,10),
( 2,1,1), ( 2,1,2), ..., ( 2, 1,10),
( 2,2,1), ( 2,2,2), ..., ( 2,10,10),
...,
(10,1,1), (10,1,2), ..., (10, 1,10),
(10,2,1), (10,2,2), ..., (10,10,10)
).filter({ case (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))
case class Name(x1: T1, x2: T2, ..., xn: Tn)
Creates a class Name
with
x1
of type T1
, ..., xn
of type Tn
Here is a case class (just a fancy record for now):
case class Novel(title: String, author: String)
You can create instances without writing new
:
scala> val n1 =
Novel ("A Feast For Crows", "George R. R. Martin")
n1: Novel =
Novel(A Feast For Crows,George R. R. Martin)
Access fields as expected:
scala> n1.author
n1: String = George R. R. Martin
Another case class:
case class Album(title: String, artist: String)
Again, 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
:
case class Movie(title: String, actors: List[String])
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
tripleThreat
?A person who has
for (n <- novels;
a <- albums;
m <- movies;
if (n.author == a.artist &&
m.actors.contains (n.author)))
yield (n.author)
tripleThreat
?for (n <- novels;
a <- albums;
m <- movies;
if (n.author == a.artist &&
m.actors.contains (n.author)))
yield (n.author)
Evaluates (not very efficiently) to:
res: List[String] = List(Steve Martin)
Databases are optimized to evaluate queries like this.
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 a SQL (database) query!
If you're curious (i.e. not on homeworks or 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 n
columns
Find a placement of n
queens so none can kill each other!
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
// same diagonal
(q1._1 - q2._1).abs == (q1._2 - q2._2).abs
canKill(q1, q2)
returns true if queen at q1
can kill q2
.queens : List[XY]
is a list of occupied positionsq : XY
is a new queen position.Which 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)(_ && _)
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.
qo
in queens
, qo
cannot kill q
queens.forall (qo => !(canKill(qo, q))
queens.forall (!(canKill(_, q))
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(q: XY, others: List[XY]) =
others forall (!canKill(_, q))
def placeQueens(columns: Int, row: Int) : List[List[XY]]
To place queens on row r
... 0
r-1
... 0
r
isSafe
w.r.t. recursive solutiondef 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)
val
s (queen
) inside for
-loop:Let's write a simple function to print a solution...
(See scala-iterators.scala
for much nicer printing function!)
def printSolution(size: Int, sol: List[XY]) =
for (i <- 1 to size) {
for (j <- 1 to size) {
if (sol contains (i,j)) print("X") else print("-")
}
println("")
}
... and a function to print the first solution (if any):
def SolveQueens(n: Int) =
for (sol <- placeQueens(n, n) take 1)
printSolution(n, sol)
scala> SolveQueens(4)
-X--
---X
X---
--X-
scala> SolveQueens(8)
X-------
----X---
-------X
-----X--
--X-----
------X-
-X------
---X----
scala> SolveQueens(2)
scala> SolveQueens(3)
scala>
There are no solutions for N = 2, N = 3
scala> def f() = println("hello")
f: ()Unit
scala> f()
hello
Can be called without parentheses:
scala> f
hello
Can also be defined such that parens must be omitted:
scala> def g = println("hello")
g: Unit
scala> g
hello
scala> g
hello
scala> g()
... error: Unit does not take parameters ... g()
Can also be defined such that parens must be omitted:
scala> def g = println("hello")
g: Unit
Note that g
is called each time, it's not the same as:
scala> val h = println("hello")
hello
h: Unit = ()
scala> h
scala> h
Iterable
vs. Iterator
Iterable
Iterable[A]
is a trait ...foreach
be implemented in all subclasses.Iterable
vs. Iterator
Iterator
Iterator[A]
is a trait ...abstract def hasNext: Boolean
abstract def next(): A
hasNext
is side-effect-free; next
is not.foreach
.for
-loopsval res3 = for (xs <- Iterator("cat", "dog");
x <- xs)
yield x
What is the type of res3
?
A. String
B. List[String]
C. Iterator[String]
D. Array[Char]
E. Iterator[Char]
for
-loopsval res3 = for (xs <- Iterator("cat", "dog");
x <- xs)
yield x
What is the type of res3
?
yield
ed value.Iterator[Char]
If class time is short, jump ahead.
But take a look at slides and Sudoku code at home!
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
5, 3, 4, 6, 7, 8, 0, 1, 2
6, 7, 2, 1, 0, 5, 3, 4, 8
1, 0, 8, 3, 4, 2, 5, 6, 7
8, 5, 0, 7, 6, 1, 4, 2, 3
4, 2, 6, 8, 5, 3, 7, 0, 1
7, 1, 3, 0, 2, 4, 8, 5, 6
0, 6, 1, 5, 3, 7, 2, 8, 4
2, 8, 7, 4, 1, 0, 6, 3, 5
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]
XY
is 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,
...
)
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
}
}
.toIterator
call?Complete implementation can be found in scala-iterators.scala
.
Contains examples of various higher-order functions, syntactic sugar, etc.
Scala is a big language, so learn by example!
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)
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)
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)
}
def pointsOfCol(x: Int) =
for (y <- 0 until 9) yield (x, y)
def pointsOfRow(y: Int) =
for (x <- 0 until 9) yield (x, y)
Elegant way to systematically:
Generate Combinations
Search Combinations
Generate & Search Combinations
Iterator
vs. List
?Iterator
vs. List
?We chose Iterator
and not List
for password cracking problem.
Not to make you write...
words.toList.filter(...).toIterator // yuck
Iterator
vs. List
?We chose Iterator
and not List
for password cracking problem.
Not to make you write...
words.toList.filter(...).toIterator // yuck
... which is the same as
words.filter(...) // sweet!
Iterator
vs. List
?We chose Iterator
and not List
for password cracking problem.
Iterator
.next
List
Iterator
vs. List
?We chose Iterator
and not List
for password cracking problem.
Iterator
"Lazily" computes one element at a time
At each call to .next
Iterator
is good for operating on big streams of dataIterator
def allVowels(megaDictionary: Iterator[String])
: Iterator[String] =
for { w <- megaDictionary if hasAllVowels(w) }
yield w
List
def allVowels(megaDictionary: List[String])
: List[String] =
for (w <- megaDictionary if hasAllVowels(w))
yield w
Iterator
uses O(1) memory (at any given time) ...List
uses O(N) memory, where N = megaDictionary
size.Iterator
?Iterator
For HW5:
for
and yield
and HOFs.Iterator
to List
.