progs/lecture3.scala
changeset 318 029e2862bb4e
parent 223 c6453f3547ec
child 320 cdfb2ce30a3d
--- a/progs/lecture3.scala	Mon Nov 11 14:04:22 2019 +0000
+++ b/progs/lecture3.scala	Tue Nov 12 00:41:00 2019 +0000
@@ -50,10 +50,137 @@
 crawl(startURL, 2)
 
 
+// User-defined Datatypes
+//========================
+
+
+abstract class Colour
+case object Red extends Colour 
+case object Green extends Colour 
+case object Blue extends Colour
+
+def fav_colour(c: Colour) : Boolean = c match {
+  case Red   => false
+  case Green => true
+  case Blue  => false 
+}
+
+fav_colour(Green)
+
+
+// ... a tiny bit more useful: Roman Numerals
+
+abstract class RomanDigit 
+case object I extends RomanDigit 
+case object V extends RomanDigit 
+case object X extends RomanDigit 
+case object L extends RomanDigit 
+case object C extends RomanDigit 
+case object D extends RomanDigit 
+case object M extends RomanDigit 
+
+type RomanNumeral = List[RomanDigit] 
+
+List(X,I)
+
+/*
+I -> 1
+II -> 2
+III  -> 3
+IV -> 4
+V -> 5
+VI -> 6
+VII -> 7
+VIII -> 8
+IX -> 9
+X -> X
+*/
+
+def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
+  case Nil => 0
+  case M::r    => 1000 + RomanNumeral2Int(r)  
+  case C::M::r => 900 + RomanNumeral2Int(r)
+  case D::r    => 500 + RomanNumeral2Int(r)
+  case C::D::r => 400 + RomanNumeral2Int(r)
+  case C::r    => 100 + RomanNumeral2Int(r)
+  case X::C::r => 90 + RomanNumeral2Int(r)
+  case L::r    => 50 + RomanNumeral2Int(r)
+  case X::L::r => 40 + RomanNumeral2Int(r)
+  case X::r    => 10 + RomanNumeral2Int(r)
+  case I::X::r => 9 + RomanNumeral2Int(r)
+  case V::r    => 5 + RomanNumeral2Int(r)
+  case I::V::r => 4 + RomanNumeral2Int(r)
+  case I::r    => 1 + RomanNumeral2Int(r)
+}
+
+RomanNumeral2Int(List(I,V))             // 4
+RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid Roman number)
+RomanNumeral2Int(List(V,I))             // 6
+RomanNumeral2Int(List(I,X))             // 9
+RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
+RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
+
+
+// another example
+//=================
+
+// Once upon a time, in a complete fictional 
+// country there were Persons...
+
+
+abstract class Person
+case object 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
+
+
+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", 84), 
+                  Peasant("Christian"), 
+                  King, 
+                  Clown)
+
+println(people.sortWith(superior).mkString("\n"))
+
+
+// String interpolations as patterns
+
+val date = "2000-01-01"
+val s"$year-$month-$day" = date
+
+def parse_date(date: String) = date match {
+  case s"$year-$month-$day" => Some((year.toInt, month.toInt, day.toInt))
+  case s"$day/$month/$year" => Some((year.toInt, month.toInt, day.toInt))
+  case _ => None
+} 
+
+
+
 
 // User-defined Datatypes and Pattern Matching
 //=============================================
 
+
+
 abstract class Exp
 case class N(n: Int) extends Exp                  // for numbers
 case class Plus(e1: Exp, e2: Exp) extends Exp
@@ -322,8 +449,37 @@
 
 
 
+// Tail Recursion
+//================
 
 
+def fact(n: Long): Long = 
+  if (n == 0) 1 else n * fact(n - 1)
+
+fact(10)              //ok
+fact(10000)           // produces a stackoverflow
+
+def factT(n: BigInt, acc: BigInt): BigInt =
+  if (n == 0) acc else factT(n - 1, n * acc)
+
+factT(10, 1)
+factT(100000, 1)
+
+// there is a flag for ensuring a function is tail recursive
+import scala.annotation.tailrec
+
+@tailrec
+def factT(n: BigInt, acc: BigInt): BigInt =
+  if (n == 0) acc else factT(n - 1, n * acc)
+
+
+
+// for tail-recursive functions the Scala compiler
+// generates loop-like code, which does not need
+// to allocate stack-space in each recursive
+// call; Scala can do this only for tail-recursive
+// functions
+