progs/lecture3.scala
changeset 320 cdfb2ce30a3d
parent 318 029e2862bb4e
child 321 7b0055205ec9
equal deleted inserted replaced
319:b84ea52bfd8f 320:cdfb2ce30a3d
     1 // Scala Lecture 3
     1 // Scala Lecture 3
     2 //=================
     2 //=================
       
     3 
       
     4 // - last week
       
     5 //
       
     6 // option type 
       
     7 // higher-order function
       
     8 
       
     9 
       
    10 
       
    11 // Recursion Again ;o)
       
    12 //====================
     3 
    13 
     4 
    14 
     5 // A Web Crawler / Email Harvester
    15 // A Web Crawler / Email Harvester
     6 //=================================
    16 //=================================
     7 //
    17 //
    13 import scala.util._
    23 import scala.util._
    14 
    24 
    15 // gets the first 10K of a web-page
    25 // gets the first 10K of a web-page
    16 def get_page(url: String) : String = {
    26 def get_page(url: String) : String = {
    17   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
    27   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
    18     getOrElse { println(s"  Problem with: $url"); ""}
    28     getOrElse { println(s" Problem with: $url"); ""}
    19 }
    29 }
    20 
    30 
    21 // regex for URLs and emails
    31 // regex for URLs and emails
    22 val http_pattern = """"https?://[^"]*"""".r
    32 val http_pattern = """"https?://[^"]*"""".r
    23 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r
    33 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r
    29 def unquote(s: String) = s.drop(1).dropRight(1)
    39 def unquote(s: String) = s.drop(1).dropRight(1)
    30 
    40 
    31 def get_all_URLs(page: String): Set[String] = 
    41 def get_all_URLs(page: String): Set[String] = 
    32   http_pattern.findAllIn(page).map(unquote).toSet
    42   http_pattern.findAllIn(page).map(unquote).toSet
    33 
    43 
    34 // naive version of crawl - searches until a given depth,
    44 // a naive version of crawl - searches until a given depth,
    35 // visits pages potentially more than once
    45 // visits pages potentially more than once
    36 
    46 def crawl(url: String, n: Int) : Unit = {
    37 def crawl(url: String, n: Int) : Set[String] = {
    47   if (n == 0) ()
       
    48   else {
       
    49     println(s"  Visiting: $n $url")
       
    50     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
       
    51   }
       
    52 }
       
    53 
       
    54 // some starting URLs for the crawler
       
    55 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
       
    56 
       
    57 crawl(startURL, 2)
       
    58 
       
    59 
       
    60 
       
    61 // a primitive email harvester
       
    62 def emails(url: String, n: Int) : Set[String] = {
    38   if (n == 0) Set()
    63   if (n == 0) Set()
    39   else {
    64   else {
    40     println(s"  Visiting: $n $url")
    65     println(s"  Visiting: $n $url")
    41     val page = get_page(url)
    66     val page = get_page(url)
    42     val new_emails = email_pattern.findAllIn(page).toSet
    67     val new_emails = email_pattern.findAllIn(page).toSet
    43     new_emails ++ 
    68     new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten
    44       (for (u <- get_all_URLs(page).par) yield crawl(u, n - 1)).flatten
    69   }
    45   }
    70 }
    46 }
    71 
    47 
    72 emails(startURL, 2)
    48 // some starting URLs for the crawler
    73 
    49 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
    74 
    50 crawl(startURL, 2)
    75 // if we want to explore the internet "deeper", then we
       
    76 // first have to parallelise the request of webpages:
       
    77 //
       
    78 // scala -cp scala-parallel-collections_2.13-0.2.0.jar 
       
    79 // import scala.collection.parallel.CollectionConverters._
       
    80 
       
    81 
       
    82 
       
    83 // another well-known example
       
    84 //============================
       
    85 
       
    86 def move(from: Char, to: Char) =
       
    87   println(s"Move disc from $from to $to!")
       
    88 
       
    89 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {
       
    90   if (n == 0) ()
       
    91   else {
       
    92     hanoi(n - 1, from, to, via)
       
    93     move(from, to)
       
    94     hanoi(n - 1, via, from, to)
       
    95   }
       
    96 } 
       
    97 
       
    98 hanoi(4, 'A', 'B', 'C')
       
    99 
       
   100 
       
   101 
       
   102 // Jumping Towers
       
   103 //================
       
   104 
       
   105 
       
   106 // the first n prefixes of xs
       
   107 // for 1 => include xs
       
   108 
       
   109 def moves(xs: List[Int], n: Int) : List[List[Int]] = (xs, n) match {
       
   110   case (Nil, _) => Nil
       
   111   case (xs, 0) => Nil
       
   112   case (x::xs, n) => (x::xs) :: moves(xs, n - 1)
       
   113 }
       
   114 
       
   115 
       
   116 moves(List(5,1,0), 1)
       
   117 moves(List(5,1,0), 2)
       
   118 moves(List(5,1,0), 5)
       
   119 
       
   120 // checks whether a jump tour exists at all
       
   121 
       
   122 def search(xs: List[Int]) : Boolean = xs match {
       
   123   case Nil => true
       
   124   case (x::xs) =>
       
   125     if (xs.length < x) true else moves(xs, x).exists(search(_))
       
   126 }
       
   127 
       
   128 
       
   129 search(List(5,3,2,5,1,1))
       
   130 search(List(3,5,1,0,0,0,1))
       
   131 search(List(3,5,1,0,0,0,0,1))
       
   132 search(List(3,5,1,0,0,0,1,1))
       
   133 search(List(3,5,1))
       
   134 search(List(5,1,1))
       
   135 search(Nil)
       
   136 search(List(1))
       
   137 search(List(5,1,1))
       
   138 search(List(3,5,1,0,0,0,0,0,0,0,0,1))
       
   139 
       
   140 // generates *all* jump tours
       
   141 //    if we are only interested in the shortes one, we could
       
   142 //    shortcircut the calculation and only return List(x) in
       
   143 //    case where xs.length < x, because no tour can be shorter
       
   144 //    than 1
       
   145 // 
       
   146 
       
   147 def jumps(xs: List[Int]) : List[List[Int]] = xs match {
       
   148   case Nil => Nil
       
   149   case (x::xs) => {
       
   150     val children = moves(xs, x)
       
   151     val results = children.map(cs => jumps(cs).map(x :: _)).flatten
       
   152     if (xs.length < x) List(x)::results else results
       
   153   }
       
   154 }
       
   155 
       
   156 jumps(List(5,3,2,5,1,1)).minBy(_.length)
       
   157 jumps(List(3,5,1,2,1,2,1))
       
   158 jumps(List(3,5,1,2,3,4,1))
       
   159 jumps(List(3,5,1,0,0,0,1))
       
   160 jumps(List(3,5,1))
       
   161 jumps(List(5,1,1))
       
   162 jumps(Nil)
       
   163 jumps(List(1))
       
   164 jumps(List(5,1,2))
       
   165 moves(List(1,2), 5)
       
   166 jumps(List(1,5,1,2))
       
   167 jumps(List(3,5,1,0,0,0,0,0,0,0,0,1))
       
   168 
       
   169 jumps(List(5,3,2,5,1,1)).minBy(_.length)
       
   170 jumps(List(1,3,5,8,9,2,6,7,6,8,9)).minBy(_.length)
       
   171 jumps(List(1,3,6,1,0,9)).minBy(_.length)
       
   172 jumps(List(2,3,1,1,2,4,2,0,1,1)).minBy(_.length)
       
   173 
       
   174 
       
   175 
       
   176 
    51 
   177 
    52 
   178 
    53 // User-defined Datatypes
   179 // User-defined Datatypes
    54 //========================
   180 //========================
    55 
   181 
    57 abstract class Colour
   183 abstract class Colour
    58 case object Red extends Colour 
   184 case object Red extends Colour 
    59 case object Green extends Colour 
   185 case object Green extends Colour 
    60 case object Blue extends Colour
   186 case object Blue extends Colour
    61 
   187 
       
   188 
    62 def fav_colour(c: Colour) : Boolean = c match {
   189 def fav_colour(c: Colour) : Boolean = c match {
    63   case Red   => false
   190   case Red   => false
    64   case Green => true
   191   case Green => true
    65   case Blue  => false 
   192   case Blue  => false 
    66 }
   193 }
    67 
   194 
    68 fav_colour(Green)
   195 fav_colour(Green)
    69 
       
    70 
   196 
    71 // ... a tiny bit more useful: Roman Numerals
   197 // ... a tiny bit more useful: Roman Numerals
    72 
   198 
    73 abstract class RomanDigit 
   199 abstract class RomanDigit 
    74 case object I extends RomanDigit 
   200 case object I extends RomanDigit 
    82 type RomanNumeral = List[RomanDigit] 
   208 type RomanNumeral = List[RomanDigit] 
    83 
   209 
    84 List(X,I)
   210 List(X,I)
    85 
   211 
    86 /*
   212 /*
    87 I -> 1
   213 I    -> 1
    88 II -> 2
   214 II   -> 2
    89 III  -> 3
   215 III  -> 3
    90 IV -> 4
   216 IV   -> 4
    91 V -> 5
   217 V    -> 5
    92 VI -> 6
   218 VI   -> 6
    93 VII -> 7
   219 VII  -> 7
    94 VIII -> 8
   220 VIII -> 8
    95 IX -> 9
   221 IX   -> 9
    96 X -> X
   222 X    -> 10
    97 */
   223 */
    98 
   224 
    99 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
   225 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
   100   case Nil => 0
   226   case Nil => 0
   101   case M::r    => 1000 + RomanNumeral2Int(r)  
   227   case M::r    => 1000 + RomanNumeral2Int(r)  
   119 RomanNumeral2Int(List(I,X))             // 9
   245 RomanNumeral2Int(List(I,X))             // 9
   120 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   246 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   121 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   247 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   122 
   248 
   123 
   249 
   124 // another example
       
   125 //=================
       
   126 
       
   127 // Once upon a time, in a complete fictional 
       
   128 // country there were Persons...
       
   129 
       
   130 
       
   131 abstract class Person
       
   132 case object King extends Person
       
   133 case class Peer(deg: String, terr: String, succ: Int) extends Person
       
   134 case class Knight(name: String) extends Person
       
   135 case class Peasant(name: String) extends Person
       
   136 
       
   137 
       
   138 def title(p: Person): String = p match {
       
   139   case King => "His Majesty the King"
       
   140   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
       
   141   case Knight(name) => s"Sir ${name}"
       
   142   case Peasant(name) => name
       
   143 }
       
   144 
       
   145 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
       
   146   case (King, _) => true
       
   147   case (Peer(_,_,_), Knight(_)) => true
       
   148   case (Peer(_,_,_), Peasant(_)) => true
       
   149   case (Peer(_,_,_), Clown) => true
       
   150   case (Knight(_), Peasant(_)) => true
       
   151   case (Knight(_), Clown) => true
       
   152   case (Clown, Peasant(_)) => true
       
   153   case _ => false
       
   154 }
       
   155 
       
   156 val people = List(Knight("David"), 
       
   157                   Peer("Duke", "Norfolk", 84), 
       
   158                   Peasant("Christian"), 
       
   159                   King, 
       
   160                   Clown)
       
   161 
       
   162 println(people.sortWith(superior).mkString("\n"))
       
   163 
       
   164 
       
   165 // String interpolations as patterns
   250 // String interpolations as patterns
   166 
   251 
   167 val date = "2000-01-01"
   252 val date = "2019-11-26"
   168 val s"$year-$month-$day" = date
   253 val s"$year-$month-$day" = date
   169 
   254 
   170 def parse_date(date: String) = date match {
   255 def parse_date(date: String) : Option[(Int, Int, Int)]= date match {
   171   case s"$year-$month-$day" => Some((year.toInt, month.toInt, day.toInt))
   256   case s"$year-$month-$day" => Some((day.toInt, month.toInt, year.toInt))
   172   case s"$day/$month/$year" => Some((year.toInt, month.toInt, day.toInt))
   257   case s"$day/$month/$year" => Some((day.toInt, month.toInt, year.toInt))
       
   258   case s"$day.$month.$year" => Some((day.toInt, month.toInt, year.toInt))
   173   case _ => None
   259   case _ => None
   174 } 
   260 } 
   175 
   261 
   176 
   262 parse_date("2019-11-26")
       
   263 parse_date("26/11/2019")
       
   264 parse_date("26.11.2019")
   177 
   265 
   178 
   266 
   179 // User-defined Datatypes and Pattern Matching
   267 // User-defined Datatypes and Pattern Matching
   180 //=============================================
   268 //=============================================
   181 
   269 
   182 
   270 // trees
   183 
   271 
   184 abstract class Exp
   272 abstract class Exp
   185 case class N(n: Int) extends Exp                  // for numbers
   273 case class N(n: Int) extends Exp                  // for numbers
   186 case class Plus(e1: Exp, e2: Exp) extends Exp
   274 case class Plus(e1: Exp, e2: Exp) extends Exp
   187 case class Times(e1: Exp, e2: Exp) extends Exp
   275 case class Times(e1: Exp, e2: Exp) extends Exp
   188 
   276 
   189 def string(e: Exp) : String = e match {
   277 def string(e: Exp) : String = e match {
   190   case N(n) => n.toString
   278   case N(n) => s"$n"
   191   case Plus(e1, e2) => "(" + string(e1) + " + " + string(e2) + ")" 
   279   case Plus(e1, e2) => s"(${string(e1)} + ${string(e2)})" 
   192   case Times(e1, e2) => "(" + string(e1) + " * " + string(e2) + ")" 
   280   case Times(e1, e2) => s"(${string(e1)} * ${string(e2)})"
   193 }
   281 }
   194 
   282 
   195 val e = Plus(N(9), Times(N(3), N(4)))
   283 val e = Plus(N(9), Times(N(3), N(4)))
   196 println(string(e))
   284 println(string(e))
   197 
   285 
   198 def eval(e: Exp) : Int = e match {
   286 def eval(e: Exp) : Int = e match {
   199   case N(n) => n
   287   case N(n) => n
   200   case Plus(e1, e2) => eval(e1) + eval(e2) 
   288   case Plus(e1, e2) => eval(e1) + eval(e2) 
   201   case Times(e1, e2) => eval(e1) * eval(e2) 
   289   case Times(e1, e2) => eval(e1) * eval(e2) 
   202 }
   290 }
       
   291 
       
   292 println(eval(e))
   203 
   293 
   204 def simp(e: Exp) : Exp = e match {
   294 def simp(e: Exp) : Exp = e match {
   205   case N(n) => N(n)
   295   case N(n) => N(n)
   206   case Plus(e1, e2) => (simp(e1), simp(e2)) match {
   296   case Plus(e1, e2) => (simp(e1), simp(e2)) match {
   207     case (N(0), e2s) => e2s
   297     case (N(0), e2s) => e2s
   215     case (e1s, N(1)) => e1s
   305     case (e1s, N(1)) => e1s
   216     case (e1s, e2s) => Times(e1s, e2s)
   306     case (e1s, e2s) => Times(e1s, e2s)
   217   }  
   307   }  
   218 }
   308 }
   219 
   309 
   220 println(eval(e))
       
   221 
   310 
   222 val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))
   311 val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))
   223 println(string(e2))
   312 println(string(e2))
   224 println(string(simp(e2)))
   313 println(string(simp(e2)))
       
   314 
   225 
   315 
   226 // Tokens and Reverse Polish Notation
   316 // Tokens and Reverse Polish Notation
   227 abstract class Token
   317 abstract class Token
   228 case class T(n: Int) extends Token
   318 case class T(n: Int) extends Token
   229 case object PL extends Token
   319 case object PL extends Token
   251   case  "*" => TI
   341   case  "*" => TI
   252   case  _ => T(s.toInt) 
   342   case  _ => T(s.toInt) 
   253 }
   343 }
   254 
   344 
   255 comp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil)
   345 comp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil)
   256 
       
   257 
       
   258 
       
   259 
       
   260 def string(e: Exp) : String = e match {
       
   261   case N(n) => n.toString
       
   262   case Plus(e1, e2) => "(" + string(e1) + " + " + string(e2) + ")"
       
   263   case Times(e1, e2) => "(" + string(e1) + " * " + string(e2) + ")"
       
   264 }
       
   265 
       
   266 val e = Plus(N(9), Times(N(3), N(4)))
       
   267 
       
   268 println(string(e))
       
   269 
       
   270 def eval(e: Exp) : Int = e match {
       
   271   case N(n) => n
       
   272   case Plus(e1, e2) => eval(e1) + eval(e2)
       
   273   case Times(e1, e2) => eval(e1) * eval(e2)
       
   274 }
       
   275 
       
   276 eval(e)
       
   277 
       
   278 def simp(e: Exp) : Exp = e match {
       
   279   case N(n) => N(n)
       
   280   case Plus(e1, e2) => (simp(e1), simp(e2)) match {
       
   281     case (N(0), e2s) => e2s
       
   282     case (e1s, N(0)) => e1s
       
   283     case (e1s, e2s) => Plus(e1s, e2s) 
       
   284   }
       
   285   case Times(e1, e2) => (simp(e1), simp(e2)) match {
       
   286     case (N(0), e2s) => N(0)
       
   287     case (e1s, N(0)) => N(0)
       
   288     case (N(1), e2s) => e2s
       
   289     case (e1s, N(1)) => e1s
       
   290     case (e1s, e2s) => Times(e1s, e2s) 
       
   291   }
       
   292 }
       
   293 
       
   294 
       
   295 val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9)))
       
   296 println(string(e2))
       
   297 println(string(simp(e2)))
       
   298 
       
   299 // Token and Reverse Polish Notation
       
   300 abstract class Token
       
   301 case class T(n: Int) extends Token
       
   302 case object PL extends Token
       
   303 case object TI extends Token
       
   304 
       
   305 def rp(e: Exp) : List[Token] = e match {
       
   306   case N(n) => List(T(n))
       
   307   case Plus(e1, e2) => rp(e1) ::: rp(e2) ::: List(PL)
       
   308   case Times(e1, e2) => rp(e1) ::: rp(e2) ::: List(TI)
       
   309 }
       
   310 
       
   311 def comp(ts: List[Token], stk: List[Int]) : Int = (ts, stk) match {
       
   312   case (Nil, st) => st.head
       
   313   case (T(n)::rest, st) => comp(rest, n::st)
       
   314   case (PL::rest, n1::n2::st) => comp(rest, n1 + n2::st)
       
   315   case (TI::rest, n1::n2::st) => comp(rest, n1 * n2::st)
       
   316 }
       
   317 
       
   318 def exp(ts: List[Token], st: List[Exp]) : Exp = (ts, st) match {
       
   319   case (Nil, st) => st.head
       
   320   case (T(n)::rest, st) => exp(rest, N(n)::st)
       
   321   case (PL::rest, n1::n2::st) => exp(rest, Plus(n2, n1)::st)
       
   322   case (TI::rest, n1::n2::st) => exp(rest, Times(n2, n1)::st)
       
   323 }
       
   324 
       
   325 exp(toks(e2), Nil)
       
   326 
       
   327 def proc(s: String) = s match {
       
   328   case "+" => PL
       
   329   case "*" => TI
       
   330   case n => T(n.toInt)
       
   331 }
       
   332 
       
   333 
       
   334 string(exp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil))
       
   335 
       
   336 
       
   337 
       
   338 // Tail recursion
       
   339 //================
       
   340 
       
   341 
       
   342 def fact(n: Long): Long = 
       
   343   if (n == 0) 1 else n * fact(n - 1)
       
   344 
       
   345 def factB(n: BigInt): BigInt = 
       
   346   if (n == 0) 1 else n * factB(n - 1)
       
   347 
       
   348 factB(100000)
       
   349 
       
   350 fact(10)              //ok
       
   351 fact(10000)           // produces a stackoverflow
       
   352 
       
   353 def factT(n: BigInt, acc: BigInt): BigInt =
       
   354   if (n == 0) acc else factT(n - 1, n * acc)
       
   355 
       
   356 factT(10, 1)
       
   357 println(factT(100000, 1))
       
   358 
       
   359 // there is a flag for ensuring a function is tail recursive
       
   360 import scala.annotation.tailrec
       
   361 
       
   362 @tailrec
       
   363 def factT(n: BigInt, acc: BigInt): BigInt =
       
   364   if (n == 0) acc else factT(n - 1, n * acc)
       
   365 
       
   366 
       
   367 
       
   368 // for tail-recursive functions the Scala compiler
       
   369 // generates loop-like code, which does not need
       
   370 // to allocate stack-space in each recursive
       
   371 // call; Scala can do this only for tail-recursive
       
   372 // functions
       
   373 
       
   374 
       
   375 
       
   376 // Jumping Towers
       
   377 //================
       
   378 
       
   379 
       
   380 // the first n prefixes of xs
       
   381 // for 1 => include xs
       
   382 
       
   383 
       
   384 
       
   385 def moves(xs: List[Int], n: Int) : List[List[Int]] = (xs, n) match {
       
   386   case (Nil, _) => Nil
       
   387   case (xs, 0) => Nil
       
   388   case (x::xs, n) => (x::xs) :: moves(xs, n - 1)
       
   389 }
       
   390 
       
   391 
       
   392 moves(List(5,1,0), 1)
       
   393 moves(List(5,1,0), 2)
       
   394 moves(List(5,1,0), 5)
       
   395 
       
   396 // checks whether a jump tour exists at all
       
   397 
       
   398 def search(xs: List[Int]) : Boolean = xs match {
       
   399   case Nil => true
       
   400   case (x::xs) =>
       
   401     if (xs.length < x) true else moves(xs, x).exists(search(_))
       
   402 }
       
   403 
       
   404 
       
   405 search(List(5,3,2,5,1,1))
       
   406 search(List(3,5,1,0,0,0,1))
       
   407 search(List(3,5,1,0,0,0,0,1))
       
   408 search(List(3,5,1,0,0,0,1,1))
       
   409 search(List(3,5,1))
       
   410 search(List(5,1,1))
       
   411 search(Nil)
       
   412 search(List(1))
       
   413 search(List(5,1,1))
       
   414 search(List(3,5,1,0,0,0,0,0,0,0,0,1))
       
   415 
       
   416 // generates *all* jump tours
       
   417 //    if we are only interested in the shortes one, we could
       
   418 //    shortcircut the calculation and only return List(x) in
       
   419 //    case where xs.length < x, because no tour can be shorter
       
   420 //    than 1
       
   421 // 
       
   422 
       
   423 def jumps(xs: List[Int]) : List[List[Int]] = xs match {
       
   424   case Nil => Nil
       
   425   case (x::xs) => {
       
   426     val children = moves(xs, x)
       
   427     val results = children.map((cs) => jumps(cs).map(x :: _)).flatten
       
   428     if (xs.length < x) List(x) :: results else results
       
   429   }
       
   430 }
       
   431 
       
   432 println(jumps(List(5,3,2,5,1,1)).minBy(_.length))
       
   433 jumps(List(3,5,1,2,1,2,1))
       
   434 jumps(List(3,5,1,2,3,4,1))
       
   435 jumps(List(3,5,1,0,0,0,1))
       
   436 jumps(List(3,5,1))
       
   437 jumps(List(5,1,1))
       
   438 jumps(Nil)
       
   439 jumps(List(1))
       
   440 jumps(List(5,1,2))
       
   441 moves(List(1,2), 5)
       
   442 jumps(List(1,5,1,2))
       
   443 jumps(List(3,5,1,0,0,0,0,0,0,0,0,1))
       
   444 
       
   445 jumps(List(5,3,2,5,1,1)).minBy(_.length)
       
   446 jumps(List(1,3,5,8,9,2,6,7,6,8,9)).minBy(_.length)
       
   447 jumps(List(1,3,6,1,0,9)).minBy(_.length)
       
   448 jumps(List(2,3,1,1,2,4,2,0,1,1)).minBy(_.length)
       
   449 
       
   450 
       
   451 
       
   452 // Tail Recursion
       
   453 //================
       
   454 
       
   455 
       
   456 def fact(n: Long): Long = 
       
   457   if (n == 0) 1 else n * fact(n - 1)
       
   458 
       
   459 fact(10)              //ok
       
   460 fact(10000)           // produces a stackoverflow
       
   461 
       
   462 def factT(n: BigInt, acc: BigInt): BigInt =
       
   463   if (n == 0) acc else factT(n - 1, n * acc)
       
   464 
       
   465 factT(10, 1)
       
   466 factT(100000, 1)
       
   467 
       
   468 // there is a flag for ensuring a function is tail recursive
       
   469 import scala.annotation.tailrec
       
   470 
       
   471 @tailrec
       
   472 def factT(n: BigInt, acc: BigInt): BigInt =
       
   473   if (n == 0) acc else factT(n - 1, n * acc)
       
   474 
       
   475 
       
   476 
       
   477 // for tail-recursive functions the Scala compiler
       
   478 // generates loop-like code, which does not need
       
   479 // to allocate stack-space in each recursive
       
   480 // call; Scala can do this only for tail-recursive
       
   481 // functions
       
   482 
       
   483 
   346 
   484 
   347 
   485 
   348 
   486 
   349 
   487 // Sudoku 
   350 // Sudoku 
   553 
   416 
   554 def search(game: String): List[String] = {
   417 def search(game: String): List[String] = {
   555   if (isDone(game)) List(game)
   418   if (isDone(game)) List(game)
   556   else {
   419   else {
   557     val cs = candidates(game, emptyPosition(game))
   420     val cs = candidates(game, emptyPosition(game))
   558     cs.par.map(c => search(update(game, empty(game), c))).toList.flatten
   421     cs.map(c => search(update(game, empty(game), c))).toList.flatten
   559   }
   422   }
   560 }
   423 }
   561 
   424 
   562 search(game0).map(pretty)
   425 search(game0).map(pretty)
   563 
   426 
   607   ((end - start) / 1.0e9) + " secs"
   470   ((end - start) / 1.0e9) + " secs"
   608 }
   471 }
   609 
   472 
   610 time_needed(1, search(game2))
   473 time_needed(1, search(game2))
   611 
   474 
       
   475 
       
   476 
       
   477 
       
   478 // Tail recursion
       
   479 //================
       
   480 
       
   481 
       
   482 def fact(n: Long): Long = 
       
   483   if (n == 0) 1 else n * fact(n - 1)
       
   484 
       
   485 def factB(n: BigInt): BigInt = 
       
   486   if (n == 0) 1 else n * factB(n - 1)
       
   487 
       
   488 factB(100000)
       
   489 
       
   490 fact(10)              //ok
       
   491 fact(10000)           // produces a stackoverflow
       
   492 
       
   493 def factT(n: BigInt, acc: BigInt): BigInt =
       
   494   if (n == 0) acc else factT(n - 1, n * acc)
       
   495 
       
   496 factT(10, 1)
       
   497 println(factT(100000, 1))
       
   498 
       
   499 // there is a flag for ensuring a function is tail recursive
       
   500 import scala.annotation.tailrec
       
   501 
       
   502 @tailrec
       
   503 def factT(n: BigInt, acc: BigInt): BigInt =
       
   504   if (n == 0) acc else factT(n - 1, n * acc)
       
   505 
       
   506 
       
   507 
       
   508 // for tail-recursive functions the Scala compiler
       
   509 // generates loop-like code, which does not need
       
   510 // to allocate stack-space in each recursive
       
   511 // call; Scala can do this only for tail-recursive
       
   512 // functions
       
   513 
   612 // tail recursive version that searches 
   514 // tail recursive version that searches 
   613 // for all solutions
   515 // for all solutions
   614 
   516 
   615 def searchT(games: List[String], sols: List[String]): List[String] = games match {
   517 def searchT(games: List[String], sols: List[String]): List[String] = games match {
   616   case Nil => sols
   518   case Nil => sols