Compilation and Running
More on Iteration and Recursion
To make an executable, put the functions in a file
e.g. lec-iterators.scala
object Freq { ... }compile with
$ scalac lec-iterators.scalaand run with
$ scala Freq lec-iterators.markdown
$ scala Freq foo.txt
...Or run in the REPL
scala> import Freq._
scala> val m = Freq("caterpillar")
scala> Freq.show(m, 1)
c : #
a : ##
e : #
i : #
r : ##
t : #
l : ##
p : #Elegant way to systematically
Lets write a polymorphic frequency finder.
def freq[A](xs: Iterable[A]): HashMap[A, Int] = {
  val freqMap = new HashMap[A, Int]
  for (x <- xs) {
    freqMap(x) = 1 + freqMap.getOrElse(x, 0)
  }
  freqMap
}Iterable[A] describes objects that can be iterated over...
Can run it on Strings
scala> freq("caterillar")
res: scala.collection.mutable.HashMap[Char,Int]
  = Map(c -> 1, a -> 2, e -> 1, i -> 1, r -> 2, t -> 1, l -> 2)or List
scala> freq(List(1,2,1,13,1,2,1,3,31,12,1))
res: scala.collection.mutable.HashMap[Int,Int]
     = Map(12 -> 1, 3 -> 1, 13 -> 1, 1 -> 5, 2 -> 2, 31 -> 1)or ...
Elegant way to systematically
Generate Combinations
Search Combinations
Generate & Search Combinations
for-loopsOf course, you can nest for-loops too:
val res = for (xs <- Array("cat", "dog");
               x  <- xs)
          yield xWhat is the type of res?
A. String
B. List[String]
C. Iterator[String]
D. List[Char]
E. Iterator[Char]
for-loopsOf course, you can nest for-loops too:
val res = for (xs <- Iterator("cat", "dog", "mouse");
               x  <- xs)
          yield xWhat is the type of res?
yielded valueIterator[Char]for-loopsval res = for ( i <- List(1, 2)
              ; j <- Iterator("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"))
for Loopsval res = for ( i <- List(1, 2)
              ; j <- List("a", "b"))
          yield (i, j)Here is how it goes down:
i <- 1j <- ayield (1, "a")j <- byield (1, "b")i <- 2j <- ayield (2, "a")j <- byield (2, "b")List((1,"a"), (1,"b"), (2,"a"), (2,"b"))Iterator vs. ListIterator vs. ListWe chose Iterator and not List for password crack
words.toList.filter(...).toIterator     // yuckIterator vs. ListWe chose Iterator and not List for password crack
words.toList.filter(...).toIterator     // yuckwords.filter(...)                       // sweet!Iterator vs. ListWe chose Iterator and not List for password crack
Iterator lazily computes one element at a timeAt each .next call
List does)List does)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 streamsIterator vs. ListIterator is lazy, good for operating on big data streamsdef allVowels(megaDictionary: Iterator[String]): Iterator[String] =
  for { w <- megaDictionary
      ; if hasAllVowels(w)  }
  yield wIterator uses O(1) memory (at any given time) ...def allVowels(megaDictionary: List[String]): List[String] =
  for (w <- megaDictionary
       if hasAllVowels(w))
  yield wList 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 streamsdef txReverse(megaDictionary: Iterator[String]): Iterator[String] =
  for (w <- megaDictionary
       c <- Iterator(w, reverse(w))
  yield cIterator uses O(1) memory (at any given time) ...def txReverse(megaDictionary: List[String]): List[String] =
  for (w <- megaDictionary
       c <- Iterator(w, reverse(w))
  yield cList 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 ?IteratorFor PA5
for and yield and HOFSIterator to Listdef 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. Nothing (Infinite Loop)
C. List()
D. List("0", "1")
E. List("00", "01", "10", "11")
Consider the following function:
def bitStrings(n: Int) : List[String] =
  if (n <= 0) { List() } else {
   for (c <- List("0", "1");
        s <- bitStrings(n-1))
   yield(c + s)
  }Here is what happens (lets work backwards)
bitStrings(0) ==> List()
Consider the following function:
def bitStrings(n: Int) : List[String] =
  if (n <= 0) { List() } else {
   for (c <- List("0", "1");
        s <- bitStrings(n-1))
   yield(c + s)
  }Here is what happens (lets work backwards)
bitStrings(1) ==> ?
c <- "0"s <- ... Nothing! (because bitStrings(0) ==> List())c <- "1"s <- ... Nothing! (because bitStrings(0) ==> List())Consider the following function:
def bitStrings(n: Int) : List[String] =
  if (n <= 0) { List() } else {
   for (c <- List("0", "1");
        s <- bitStrings(n-1))
   yield(c + s)
  }Here is what happens (lets work backwards)
bitStrings(1) ==> List()
c <- "0"s <- ... Nothing! (because bitStrings(0) ==> List())c <- "1"s <- ... Nothing! (because bitStrings(0) ==> 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(1,2)
           ; j <- List())
       yield (i, j)
res1: List[(Int, String)] = List()or the second sequence is empty
scala> for ( i <- List()
           ; j <- List("a", "b"))
       yield (i, j)
res2: List[(Nothing, String)] = List()def bitStrs(n: Int) : List[String] =
  if (n <= 0) { List() } else {
   for (c <- List("0", "1");
        s <- bitStrs(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")
etc. ?
bitStrs(0))""0def bitStrings(n: Int) : List[String] =
  if (n <= 0) { List("") } else {
   for (c <- List("0", "1");
        s <- bitStrings(n-1))
   yield(c + s)
  }res ?def foo(w: String) : List[String] =
  if (w == "")  { List("") } else
    for (c <- List("", w.substring(0, 1));
         s <- foo(w.substring(1)))
    yield(c + s)
val res = foo("go")Hint "sunday".substring(0, 1) ==> "s" and "sunday".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, lets work backwards
subStrings("") ==> List("")
def subStrings(w: String) : List[String] =
  if (w == "")  { List(w) } else
    for (c <- List("", w.substring(0, 1));
         s <- foo(w.substring(1)))
    yield(c + s)Lets work backwards . subStrings("o") ==> ?
c <- ""s <- "" (as subStrings("") ==> List(""))yield ("" + "")c <- "o"s <- "" (as subStrings("") ==> List(""))yield ("o" + "")def subStrings(w: String) : List[String] =
  if (w == "")  { List(w) } else
    for (c <- List("", w.substring(0, 1));
         s <- foo(w.substring(1)))
    yield(c + s)Lets work backwards . subStrings("o") ==> List("", "o")
c <- ""s <- "" (because subStrings("") ==> List(""))\(\quad \quad\) yield ("" + "")
c <- "o"s <- "" (because subStrings("") ==> List(""))\(\quad \quad\) yield ("o" + "")
def subStrings(w: String) : List[String] =
  if (w == "")  { List(w) } else
    for (c <- List("", w.substring(0, 1));
         s <- foo(w.substring(1)))
    yield(c + s)Lets work backwards . subStrings("go") ==> ?
c <- ""s <- "" (because subStrings("o") ==> List("", "o"))yield ("" + "")s <- "o" (because subStrings("o") ==> List("", "o"))yield ("" + "o")c <- "g"s <- "" (because subStrings("o") ==> List("", "o"))yield ("g" + "")s <- "o" (because subStrings("o") ==> List("", "o"))yield ("g" + "o")def subStrings(w: String) : List[String] =
  if (w == "")  { List(w) } else
    for (c <- List("", w.substring(0, 1));
         s <- foo(w.substring(1)))
    yield(c + s)Lets work backwards . subStrings("go") ==> List("", "o", "g", "go")
c <- ""s <- "" (because subStrings("o") ==> List("", "o"))yield ("" + "")s <- "o" (because subStrings("o") ==> List("", "o"))yield ("" + "o")c <- "g"s <- "" (because subStrings("o") ==> List("", "o"))yield ("g" + "")s <- "o" (because subStrings("o") ==> List("", "o"))yield ("g" + "o")substrings is almost identical to transformCapitalize
subStringsReplace each character "c" by either "c" or ""
transformCapitalizeReplace each character "c" by either "c" or "C"
transformDigitsElegant 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}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 { 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,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 { 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))
i <= j to avoid ordering issues.How would you avoid duplicates?
Only count tuples where i <= j to avoid ordering issues.
val res = for (i <- 1 to 10;
               j <- 1 to 10;
               k <- 1 to 10
               if i*i + j*j == k*k && i <= j)
          yield (i, j, k)case class Name(x1: T1, x2: T2, ..., xn: TN)Creates a class Name with
x1 of type T1 ... xn of type TnNovelHere 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 because Scala generates an apply for us)
scala> val n1 = Novel("A Feast For Crows", "George R. R. Martin")
n1: Novel = Novel(A Feast For Crows,George R. R. Martin)
// which is equivalent from Scala's view to...
scala> val n2 = Novel.apply("A Feast For Crows", "George R. R. Martin")
n2: Novel = Novel(A Feast For Crows,George R. R. Martin)and access fields like so
scala> n1.author
n1: String = George R. R. MartinAlbumHere 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 again)
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
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)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 ...!
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 diagonalcanKill(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)(_ && _)
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 qqueens.forall (qo => !(canKill(qo, q))queens.forall (!(canKill(_, q))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]To place queens on row r ... 0
r-1 ... 0risSafe w.r.t. recursive solutionGiven a chess board of size n rows and columns
Find a placement of n queens so none can kill each other!
r-1...0r !r-1...0r !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
r-1 ... 0ryield position which isSafe w.r.t. recursive solutionGiven 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)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
OXOOGiven 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
XOOOOOOOGiven 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>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, 05, 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, 0The 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, 0is 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
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
  }
}.toIterator call?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