progs/lecture2.scala
changeset 55 6610c1dfa8a9
parent 53 9f8751912560
child 56 6fa91be92d0c
--- a/progs/lecture2.scala	Wed Nov 16 23:34:36 2016 +0000
+++ b/progs/lecture2.scala	Thu Nov 17 03:10:44 2016 +0000
@@ -2,6 +2,25 @@
 //=================
 
 
+// Implicits
+//===========
+//
+// for example adding your own methods to Strings:
+// imagine you want to increment strings, like
+//
+//     "HAL".increment
+//
+// you can avoid ugly fudges, like a MyString, by
+// using implicit conversions
+
+
+implicit class MyString(s: String) {
+  def increment = for (c <- s) yield (c + 1).toChar 
+}
+
+"HAL".increment
+
+
 
 // Option type
 //=============
@@ -74,7 +93,6 @@
 
 
 
-
 // Type abbreviations
 //====================
 
@@ -85,7 +103,6 @@
 
 
 
-
 // No return in Scala
 //====================
 
@@ -110,6 +127,8 @@
   x * x
 }
 
+
+
 // Pattern Matching
 //==================
 
@@ -151,6 +170,65 @@
   case _ => "many"
 }
 
+// User-defined Datatypes
+//========================
+
+abstract class Tree
+case class Node(elem: Int, left: Tree, right: Tree) extends Tree
+case class Leaf() extends Tree
+
+def insert(tr: Tree, n: Int): Tree = tr match {
+  case Leaf() => Node(n, Leaf(), Leaf())
+  case Node(m, left, right) => 
+    if (n == m) Node(m, left, right) 
+    else if (n < m) Node(m, insert(left, n), right)
+    else Node(m, left, insert(right, n))
+}
+
+
+val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf()))
+insert(t1, 3)
+
+def balance(tr: Tree): Int = tr match {
+  case Leaf() => 0
+  case Node(_, left, right) => balance(left) - balance(right)
+}
+
+
+// another example
+
+abstract class Person
+case class King() extends Person
+case class Peer(deg: String, terr: String, succ: Int) extends Person
+case class Knight(name: String) extends Person
+case class Peasant(name: String) extends Person
+case class Clown() extends Person
+
+def title(p: Person): String = p match {
+  case King() => "His Majesty the King"
+  case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
+  case Knight(name) => s"Sir ${name}"
+  case Peasant(name) => name
+}
+
+def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
+  case (King(), _) => true
+  case (Peer(_,_,_), Knight(_)) => true
+  case (Peer(_,_,_), Peasant(_)) => true
+  case (Peer(_,_,_), Clown()) => true
+  case (Knight(_), Peasant(_)) => true
+  case (Knight(_), Clown()) => true
+  case (Clown(), Peasant(_)) => true
+  case _ => false
+}
+
+val people = List(Knight("David"), 
+                  Peer("Duke", "Norfolk", 42), 
+                  Peasant("Christian"), 
+                  King(), 
+                  Clown())
+
+println(people.sortWith(superior(_, _)))
 
 // Higher-Order Functions
 //========================
@@ -174,34 +252,141 @@
 
 lst.map(square).filter(_ > 4)
 
+lst.map(square).filter(_ > 4).map(square)
 
+// in my collatz.scala
+//(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1)
+
+
+// type of functions
+def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match {
+  case Nil => Nil
+  case x::xs => f(x)::my_map_int(xs, f)
+}
+
+my_map_int(lst, square)
+
+
+def sumOf(f: Int => Int, lst: List[Int]): Int = lst match {
+  case Nil => 0
+  case x::xs => f(x) + sumOf(f, xs)
+}
+
+def sum_squares(lst: List[Int]) = sumOf(square, lst)
+def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
+
+sum_squares(lst)
+sum_cubes(lst)
 
 
 // Sudoku
 //========
 
+val game0 = """.14.6.3..
+              |62...4..9
+              |.8..5.6..
+              |.6.2....3
+              |.7..1..5.
+              |5....9.6.
+              |..6.2..3.
+              |1..5...92
+              |..7.9.41.""".stripMargin.replaceAll("\\n", "")
+
+
+type Pos = (Int, Int)
+val EmptyValue = '.'
+val MaxValue = 9
+
+val allValues = "123456789".toList
+val indexes = (0 to 8).toList
+
+def empty(game: String) = game.indexOf(EmptyValue)
+def emptyPosition(game: String) = (empty(game) % MaxValue, empty(game) / MaxValue)
+def isDone(game: String) = empty(game) == -1 
+
+def row(game: String, y: Int): List[Char] = indexes.map(col => game(y * MaxValue + col))
+def col(game: String, x: Int): List[Char] = indexes.map(row => game(x + row * MaxValue))
+
+def box(game: String, pos: Pos): List[Char] = {
+    def base(p: Int): Int = (p / 3) * 3
+    val x0 = base(pos._1)
+    val y0 = base(pos._2)
+    val ys = (y0 until y0 + 3).toList
+    (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue)))
+}
+
+
+//row(game0, 0)
+//row(game0, 1)
+//box(game0, (3,1))
+
+def update(game: String, pos: Int, value: Char): String = game.updated(pos, value)
+
+def toAvoid(game: String, pos: Pos): List[Char] = 
+  (col(game, pos._1) ++ row(game, pos._2) ++ box(game, pos)).distinct
+
+def candidates(game: String, pos: Pos): List[Char] = allValues diff toAvoid(game,pos)
+
+//candidates(game0, (0,0))
+
+def pretty(game: String): String = "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
+
+def search(game: String): List[String] = {
+  if (isDone(game)) List(game)
+  else 
+    candidates(game, emptyPosition(game)).par.map(c => search(update(game, empty(game), c))).toList.flatten
+}
+
+
+val game1 = """23.915...
+              |...2..54.
+              |6.7......
+              |..1.....9
+              |89.5.3.17
+              |5.....6..
+              |......9.5
+              |.16..7...
+              |...329..1""".stripMargin.replaceAll("\\n", "")
+
+// game that is in the hard category
+val game2 = """8........
+              |..36.....
+              |.7..9.2..
+              |.5...7...
+              |....457..
+              |...1...3.
+              |..1....68
+              |..85...1.
+              |.9....4..""".stripMargin.replaceAll("\\n", "")
+
+// game with multiple solutions
+val game3 = """.8...9743
+              |.5...8.1.
+              |.1.......
+              |8....5...
+              |...8.4...
+              |...3....6
+              |.......7.
+              |.3.5...8.
+              |9724...5.""".stripMargin.replaceAll("\\n", "")
+
+search(game0).map(pretty)
+search(game1).map(pretty)
+
+// for measuring time
+def time_needed[T](i: Int, code: => T) = {
+  val start = System.nanoTime()
+  for (j <- 1 to i) code
+  val end = System.nanoTime()
+  ((end - start) / i / 1.0e9) + " secs"
+}
+
+search(game2).map(pretty)
+search(game3).distinct.map(pretty).length
+time_needed(3, search(game2))
+time_needed(3, search(game3))
+
 
 
 
 
-//sorting, higher-order functions
-//lexicographic ordering
-
-
-// Implicits
-//===========
-//
-// for example adding your own methods to Strings:
-// imagine you want to increment strings, like
-//
-//     "HAL".increment
-//
-// you can avoid ugly fudges, like a MyString, by
-// using implicit conversions
-
-
-implicit class MyString(s: String) {
-  def increment = for (c <- s) yield (c + 1).toChar 
-}
-
-"HAL".increment