progs/lecture3.scala
changeset 506 28f78d9081d7
parent 491 2a30c7dfe3ed
equal deleted inserted replaced
505:c06b45a52d50 506:28f78d9081d7
     2 //=================
     2 //=================
     3 
     3 
     4 // last week:
     4 // last week:
     5 // higher-order functions
     5 // higher-order functions
     6 // maps
     6 // maps
     7 
     7 // recursion
     8 // - recursion
     8 
     9 // - Sudoku
     9 
    10 // - string interpolations
       
    11 // - Pattern-Matching
    10 // - Pattern-Matching
       
    11 // - Datatypes 
       
    12 // - Countdown Game
       
    13 // - String interpolations
       
    14 
    12 
    15 
    13 def fact(n: BigInt) : BigInt = {
    16 def fact(n: BigInt) : BigInt = {
    14   if (n == 0) 1 
    17   if (n == 0) 1 
    15   else n * fact(n - 1)
    18   else n * fact(n - 1)
    16 }
    19 }
    19   if (n == 0) 1
    22   if (n == 0) 1
    20   else if (n == 1) 1 
    23   else if (n == 1) 1 
    21   else fib(n - 1) + fib(n - 2)
    24   else fib(n - 1) + fib(n - 2)
    22 }
    25 }
    23 
    26 
    24 
    27 def list_len(xs: List[Int]) : Int =
    25 
    28   if (xs == Nil) 0
       
    29   else 1 + list_len(xs.tail)
       
    30 
       
    31 def list_len(xs: List[Int]) : Int = xs match {
       
    32   case Nil => 0
       
    33   case _::xt => 1 + list_len(xt)
       
    34 }
       
    35 
       
    36 def list_len(xs: List[Int]) : Int =
       
    37   if (xs == Nil) 0
       
    38   else 1 + list_len(xs.tail)
       
    39 
       
    40 
       
    41 def list_map(xs: List[Int], f: Int => Int) : List[Int] =
       
    42   if (xs == Nil) Nil
       
    43   else f(xs.head) :: list_map(xs.tail, f)
       
    44 
       
    45 
       
    46 
       
    47 def altproduct(xs: List[Int]) : List[Int] = xs match {
       
    48   case Nil => Nil
       
    49   case (x::y::xs) => x * y :: altproduct(y::xs)
       
    50   case (x::Nil) => List(x)
       
    51 }
       
    52 
       
    53 altproduct(List(1,2,3,4,5))
       
    54 
       
    55 
       
    56 def powerset(xs: Set[Int]) : Set[Set[Int]] = {
       
    57   if (xs == Set()) Set(Set())
       
    58   else {
       
    59     val ps = powerset(xs.tail)
       
    60     ps ++ ps.map(_ + xs.head)
       
    61   }
       
    62 }     
       
    63 
       
    64 powerset(Set(1,2,3)).mkString("\n")
    26 
    65 
    27 
    66 
    28 def inc(n: Int) : Int = n + 1
    67 def inc(n: Int) : Int = n + 1
    29 
    68 
    30 for (n <- List(1,2,3,4)) yield inc(n)
    69 for (n <- List(1,2,3,4)) yield inc(n)
    32 List().map(inc)
    71 List().map(inc)
    33 
    72 
    34 
    73 
    35 
    74 
    36 my_map(inc, List(1,2,3,4))
    75 my_map(inc, List(1,2,3,4))
    37 
       
    38 
       
    39 
       
    40 
       
    41 
       
    42 
       
    43 // A Recursive Web Crawler / Email Harvester
       
    44 //===========================================
       
    45 //
       
    46 // the idea is to look for links using the
       
    47 // regular expression "https?://[^"]*" and for
       
    48 // email addresses using another regex.
       
    49 
       
    50 import io.Source
       
    51 import scala.util._
       
    52 
       
    53 // gets the first 10K of a web-page
       
    54 def get_page(url: String) : String = {
       
    55   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
       
    56     getOrElse { println(s"  Problem with: $url"); ""}
       
    57 }
       
    58 
       
    59 // regex for URLs and emails
       
    60 val http_pattern = """"https?://[^"]*"""".r
       
    61 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r
       
    62 
       
    63 //test case:
       
    64 //email_pattern.findAllIn
       
    65 //  ("foo bla christian@kcl.ac.uk 1234567").toList
       
    66 
       
    67 
       
    68 // drops the first and last character from a string
       
    69 def unquote(s: String) = s.drop(1).dropRight(1)
       
    70 
       
    71 def get_all_URLs(page: String): Set[String] = 
       
    72   http_pattern.findAllIn(page).map(unquote).toSet
       
    73 
       
    74 // naive version of crawl - searches until a given depth,
       
    75 // visits pages potentially more than once
       
    76 def crawl(url: String, n: Int) : Unit = {
       
    77   if (n == 0) ()
       
    78   else {
       
    79     println(s"  Visiting: $n $url")
       
    80     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
       
    81   }
       
    82 }
       
    83 
       
    84 // some starting URLs for the crawler
       
    85 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
       
    86 
       
    87 crawl(startURL, 2)
       
    88 
       
    89 
       
    90 // a primitive email harvester
       
    91 def emails(url: String, n: Int) : Set[String] = {
       
    92   if (n == 0) Set()
       
    93   else {
       
    94     println(s"  Visiting: $n $url")
       
    95     val page = get_page(url)
       
    96     val new_emails = email_pattern.findAllIn(page).toSet
       
    97     new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten
       
    98   }
       
    99 }
       
   100 
       
   101 emails(startURL, 2)
       
   102 
       
   103 
       
   104 
       
   105 // Sudoku 
       
   106 //========
       
   107 
       
   108 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
   109 // EFFICIENT AND FAST, just explaining exhaustive
       
   110 // depth-first search
       
   111 
       
   112 //> using dep org.scala-lang.modules::scala-parallel-collections:1.0.4
       
   113 import scala.collection.parallel.CollectionConverters.*
       
   114 
       
   115 
       
   116 val s1 = "s\n"
       
   117 val s2 = """s\n"""
       
   118 
       
   119 val game0 = """.14.6.3..
       
   120               |62...4..9
       
   121               |.8..5.6..
       
   122               |.6.2....3
       
   123               |.7..1..5.
       
   124               |5....9.6.
       
   125               |..6.2..3.
       
   126               |1..5...92
       
   127               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
   128 
       
   129 type Pos = (Int, Int)
       
   130 val EmptyValue = '.'
       
   131 val MaxValue = 9
       
   132 
       
   133 def pretty(game: String): String = 
       
   134   "\n" + (game.grouped(MaxValue).mkString("\n"))
       
   135 
       
   136 pretty(game0)
       
   137 
       
   138 
       
   139 val allValues = "123456789".toList
       
   140 val indexes = (0 to 8).toList
       
   141 
       
   142 def empty(game: String) = game.indexOf(EmptyValue)
       
   143 def isDone(game: String) = empty(game) == -1 
       
   144 def emptyPosition(game: String) : Pos = {
       
   145   val e = empty(game)
       
   146   (e % MaxValue, e / MaxValue)
       
   147 }
       
   148 
       
   149 def get_row(game: String, y: Int) = 
       
   150   indexes.map(col => game(y * MaxValue + col))
       
   151 def get_col(game: String, x: Int) = 
       
   152   indexes.map(row => game(x + row * MaxValue))
       
   153 
       
   154 //get_row(game0, 0)
       
   155 //get_row(game0, 1)
       
   156 //get_col(game0, 0)
       
   157 
       
   158 def get_box(game: String, pos: Pos): List[Char] = {
       
   159     def base(p: Int): Int = (p / 3) * 3
       
   160     val x0 = base(pos._1)
       
   161     val y0 = base(pos._2)
       
   162     val ys = (y0 until y0 + 3).toList
       
   163     (x0 until x0 + 3).toList
       
   164       .flatMap(x => ys.map(y => game(x + y * MaxValue)))
       
   165 }
       
   166 
       
   167 
       
   168 //get_box(game0, (3, 1))
       
   169 
       
   170 
       
   171 // this is not mutable!!
       
   172 def update(game: String, pos: Int, value: Char): String = 
       
   173   game.updated(pos, value)
       
   174 
       
   175 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   176   (get_col(game, pos._1) ++ 
       
   177    get_row(game, pos._2) ++ 
       
   178    get_box(game, pos))
       
   179 
       
   180 def candidates(game: String, pos: Pos): List[Char] = 
       
   181   allValues.diff(toAvoid(game, pos))
       
   182 
       
   183 //candidates(game0, (0,0))
       
   184 
       
   185 
       
   186 def search(game: String): List[String] = {
       
   187   if (isDone(game)) List(game)
       
   188   else {
       
   189     val cs = candidates(game, emptyPosition(game))
       
   190     cs.par.map(c => search(update(game, empty(game), c))).flatten.toList
       
   191   }
       
   192 }
       
   193 
       
   194 pretty(game0)
       
   195 search(game0).map(pretty)
       
   196 
       
   197 val game1 = """23.915...
       
   198               |...2..54.
       
   199               |6.7......
       
   200               |..1.....9
       
   201               |89.5.3.17
       
   202               |5.....6..
       
   203               |......9.5
       
   204               |.16..7...
       
   205               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   206 
       
   207 search(game1).map(pretty)
       
   208 
       
   209 // a game that is in the hard category
       
   210 val game2 = """8........
       
   211               |..36.....
       
   212               |.7..9.2..
       
   213               |.5...7...
       
   214               |....457..
       
   215               |...1...3.
       
   216               |..1....68
       
   217               |..85...1.
       
   218               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   219 
       
   220 search(game2).map(pretty)
       
   221 
       
   222 // game with multiple solutions
       
   223 val game3 = """.8...9743
       
   224               |.5...8.1.
       
   225               |.1.......
       
   226               |8....5...
       
   227               |...8.4...
       
   228               |...3....6
       
   229               |.......7.
       
   230               |.3.5...8.
       
   231               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   232 
       
   233 search(game3).map(pretty).foreach(println)
       
   234 
       
   235 // for measuring time
       
   236 def time_needed[T](i: Int, code: => T) = {
       
   237   val start = System.nanoTime()
       
   238   for (j <- 1 to i) code
       
   239   val end = System.nanoTime()
       
   240   s"${(end - start) / 1.0e9} secs"
       
   241 }
       
   242 
       
   243 time_needed(2, search(game2))
       
   244 
       
   245 
       
   246 // concurrency 
       
   247 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar 
       
   248 // import scala.collection.parallel.CollectionConverters._
       
   249 
       
   250 
       
   251 
       
   252 
       
   253 // String Interpolations
       
   254 //=======================
       
   255 
       
   256 def cube(n: Int) : Int = n * n * n
       
   257 
       
   258 val n = 3
       
   259 println("The cube of " + n + " is " + cube(n) + ".")
       
   260 
       
   261 println(s"The cube of $n is ${cube(n)}.")
       
   262 
       
   263 // or even
       
   264 
       
   265 println(s"The cube of $n is ${n * n * n}.")
       
   266 
       
   267 // helpful for debugging purposes
       
   268 //
       
   269 //     "The most effective debugging tool is still careful 
       
   270 //          thought, coupled with judiciously placed print 
       
   271 //                                             statements."
       
   272 //       — Brian W. Kernighan, in Unix for Beginners (1979)
       
   273 
       
   274 
       
   275 def gcd_db(a: Int, b: Int) : Int = {
       
   276   println(s"Function called with $a and $b.")
       
   277   if (b == 0) a else gcd_db(b, a % b)
       
   278 }
       
   279 
       
   280 gcd_db(48, 18)
       
   281 
       
   282 
       
   283 
       
   284 
       
   285 // Recursion Again ;o)
       
   286 //====================
       
   287 
       
   288 
       
   289 // another well-known example: Towers of Hanoi
       
   290 //=============================================
       
   291 
       
   292 def move(from: Char, to: Char) =
       
   293   println(s"Move disc from $from to $to!")
       
   294 
       
   295 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {
       
   296   if (n == 0) ()
       
   297   else {
       
   298     hanoi(n - 1, from, to, via)
       
   299     move(from, to)
       
   300     hanoi(n - 1, via, from, to)
       
   301   }
       
   302 } 
       
   303 
       
   304 hanoi(4, 'A', 'B', 'C')
       
   305 
    76 
   306 
    77 
   307 
    78 
   308 // Pattern Matching
    79 // Pattern Matching
   309 //==================
    80 //==================
   367   case "December" => "It's winter"
   138   case "December" => "It's winter"
   368   case "January" | "February" => "It's unfortunately winter"
   139   case "January" | "February" => "It's unfortunately winter"
   369   case _ => "Wrong month"
   140   case _ => "Wrong month"
   370 }
   141 }
   371 
   142 
       
   143 
       
   144 season("November")
       
   145 season("abcd")
   372 // pattern-match on integers
   146 // pattern-match on integers
   373 
   147 
   374 def fib(n: Int) : Int = n match { 
   148 def fib(n: Int) : Int = n match { 
   375   case 0 | 1 => 1
   149   case 0 | 1 => 1
   376   case n => fib(n - 1) + fib(n - 2)
   150   case n => fib(n - 1) + fib(n - 2)
   423 foo(List(2,2,2,3))
   197 foo(List(2,2,2,3))
   424 
   198 
   425 
   199 
   426 // Trees
   200 // Trees
   427 
   201 
   428 abstract class Tree
   202 enum Tree {
   429 case class Leaf(x: Int) extends Tree
   203   case Leaf(x: Int) 
   430 case class Node(s: String, left: Tree, right: Tree) extends Tree 
   204   case Node(s: String, left: Tree, right: Tree)  
       
   205 }
       
   206 import Tree._
   431 
   207 
   432 val lf = Leaf(20)
   208 val lf = Leaf(20)
   433 val tr = Node("foo", Leaf(10), Leaf(23))
   209 val tr = Node("foo", Leaf(10), Leaf(23))
   434 
   210 
   435 def sizet(t: Tree) : Int = t match {
   211 def tree_size(t: Tree) : Int = t match {
   436   case Leaf(_) => 1
   212   case Leaf(_) => 1
   437   case Node(_, left , right) => 1 + sizet(left) + sizet(right)
   213   case Node(_, left , right) => 
   438 }
   214     1 + tree_size(left) + tree_size(right)
   439 
   215 }
   440 sizet(tr)
   216 
       
   217 tree_size(tr)
   441 
   218 
   442 val lst : List[Tree] = List(lf, tr)
   219 val lst : List[Tree] = List(lf, tr)
   443 
   220 
   444 
   221 
   445 abstract class Colour
   222 abstract class Colour
   611 parse_date("2019-11-26")
   388 parse_date("2019-11-26")
   612 parse_date("26/11/2019")
   389 parse_date("26/11/2019")
   613 parse_date("26.11.2019")
   390 parse_date("26.11.2019")
   614 
   391 
   615 
   392 
       
   393 // Countdown Game using Powerset
       
   394 //===============================
       
   395 
       
   396 
       
   397 def powerset(xs: Set[Int]) : Set[Set[Int]] = {
       
   398   if (xs == Set()) Set(Set())
       
   399   else {
       
   400     val ps = powerset(xs.tail)  
       
   401     ps ++ ps.map(_ + xs.head)
       
   402   }
       
   403 }  
       
   404 
       
   405 powerset(Set(1,2,3)).mkString("\n")
       
   406 
       
   407 // proper subsets
       
   408 def psubsets(xs: Set[Int]) = 
       
   409   powerset(xs) -- Set(Set(), xs) 
       
   410 
       
   411 psubsets(Set(1,2,3)).mkString("\n")
       
   412 
       
   413 def splits(xs: Set[Int]) : Set[(Set[Int], Set[Int])] =
       
   414   psubsets(xs).map(s => (s, xs -- s))
       
   415 
       
   416 splits(Set(1,2,3,4)).mkString("\n")
       
   417 
       
   418 
       
   419 enum Tree {
       
   420   case Num(i: Int)
       
   421   case Add(l: Tree, r: Tree)
       
   422   case Mul(l: Tree, r: Tree)
       
   423   case Sub(l: Tree, r: Tree)
       
   424   case Div(l: Tree, r: Tree)
       
   425 }
       
   426 import Tree._
       
   427 
       
   428 //pretty printing
       
   429 def pp(tr: Tree) : String = tr match {
       
   430   case Num(n) => s"$n"
       
   431   case Add(l, r) => s"(${pp(l)} + ${pp(r)})"
       
   432   case Mul(l, r) => s"(${pp(l)} * ${pp(r)})"
       
   433   case Sub(l, r) => s"(${pp(l)} - ${pp(r)})"
       
   434   case Div(l, r) => s"(${pp(l)} / ${pp(r)})"
       
   435 }
       
   436 
       
   437 
       
   438 def eval(tr: Tree) : Option[Int] = tr match {
       
   439   case Num(n) => Some(n)
       
   440   case Add(l, r) => 
       
   441     for (ln <- eval(l); rn <- eval(r)) yield ln + rn
       
   442   case Mul(l, r) => 
       
   443     for (ln <- eval(l); rn <- eval(r)) yield ln * rn 
       
   444   case Sub(l, r) => 
       
   445     for (ln <- eval(l); rn <- eval(r)) yield ln - rn
       
   446   case Div(l, r) => 
       
   447     for (ln <- eval(l); rn <- eval(r); if rn != 0) 
       
   448       yield ln / rn 
       
   449 }
       
   450 
       
   451 
       
   452 
       
   453 def search(nums: Set[Int]) : Set[Tree] = nums.size match {
       
   454   case 0 => Set()
       
   455   case 1 => Set(Num(nums.head))
       
   456   case 2 => {
       
   457     val ln = Num(nums.head)
       
   458     val rn = Num(nums.tail.head)
       
   459     Set(Add(ln, rn), Mul(ln, rn),
       
   460         Sub(ln, rn), Sub(rn, ln),
       
   461         Div(ln, rn), Div(rn, ln))
       
   462   }
       
   463   case n => {
       
   464     val spls = splits(nums)
       
   465     val res = 
       
   466       for ((ls, rs) <- spls) yield {
       
   467       for (lt <- search(ls);
       
   468            rt <- search(rs)) yield {
       
   469             Set(Add(lt, rt), Mul(lt, rt),
       
   470                 Sub(lt, rt), Sub(rt, lt),
       
   471                 Div(lt, rt), Div(rt, lt))
       
   472            }
       
   473     }
       
   474     res.flatten.flatten
       
   475   }
       
   476 }
       
   477 
       
   478 Set(Set(1,2), Set(2,3), Set(4,5)).flatten
       
   479 
       
   480 search(Set(1))
       
   481 search(Set(1, 2)).mkString("\n")
       
   482 search(Set(1, 2,3)).map(pp).mkString("\n")
       
   483 search(Set(1, 2,3)).map(pl).mkString("\n")
       
   484 search(Set(1, 2,3)).map(tr => s"${pp(tr)} = ${eval(tr)}").mkString("\n")
       
   485 
       
   486 
       
   487 def search(nums: Set[Int]) : Set[Tree] = nums.size match {
       
   488   case 0 => Set()
       
   489   case 1 => Set(Num(nums.head))
       
   490   case xs => {
       
   491     val spls = splits(nums)
       
   492     val subtrs = 
       
   493       for ((lspls, rspls) <- spls;
       
   494             lt <- search(lspls); 
       
   495             rt <- search(rspls)) yield {
       
   496         Set(Add(lt, rt), Mul(lt, rt))
       
   497     } 
       
   498     subtrs.flatten
       
   499   }
       
   500 }
       
   501 
       
   502 println(search(Set(1,2,3,4)).mkString("\n"))
       
   503 
       
   504 def pp(tr: Tree) : String = tr match {
       
   505   case Num(n) => s"$n"
       
   506   case Add(l, r) => s"(${pp(l)} + ${pp(r)})"
       
   507   case Mul(l, r) => s"(${pp(l)} * ${pp(r)})"
       
   508 }
       
   509 
       
   510 
       
   511 
       
   512 def eval(tr: Tree) : Int = tr match {
       
   513   case Num(n) => n
       
   514   case Add(l, r) => eval(l) + eval(r) 
       
   515   case Mul(l, r) => eval(l) * eval(r)
       
   516 }
       
   517 
       
   518 
       
   519 
       
   520 
       
   521 
       
   522 def search(nums: Set[Int]) : Set[Tree] =  nums.size match {
       
   523   case 0 => Set()
       
   524   case 1 => Set(Num(nums.head))
       
   525   case 2 => {
       
   526     val l = nums.head
       
   527     val r = nums.tail.head
       
   528     Set(Add(Num(l), Num(r)), 
       
   529         Mul(Num(l), Num(r)))
       
   530         ++ Option.when(l <= r)(Sub(Num(r), Num(l)))
       
   531         ++ Option.when(l > r)(Sub(Num(l), Num(r)))
       
   532         ++ Option.when(r > 0 && l % r == 0)(Div(Num(l), Num(r)))
       
   533         ++ Option.when(l > 0 && r % l == 0)(Div(Num(r), Num(l)))
       
   534   }
       
   535   case xs => {
       
   536     val spls = splits(nums)
       
   537     val subtrs = 
       
   538       for ((lspls, rspls) <- spls;
       
   539            lt <- search(lspls); 
       
   540           rt <- search(rspls)) yield {
       
   541         Set(Add(lt, rt), Sub(lt, rt),
       
   542             Mul(lt, rt), Div(lt, rt))
       
   543     } 
       
   544     subtrs.flatten
       
   545   }
       
   546 }
       
   547 
       
   548 println(search(Set(1,2,3,4)).mkString("\n"))
       
   549 
       
   550 def eval(tr: Tree) : Option[Int] = tr match {
       
   551   case Num(n) => Some(n)
       
   552   case Add(l, r) => 
       
   553     for (rl <- eval(l); rr <- eval(r)) yield rl + rr
       
   554   case Mul(l, r) => 
       
   555     for (rl <- eval(l); rr <- eval(r)) yield rl * rr  
       
   556   case Sub(l, r) => 
       
   557     for (rl <- eval(l); rr <- eval(r);
       
   558          if 0 <= rl - rr) yield rl - rr   
       
   559   case Div(l, r) => 
       
   560     for (rl <- eval(l); rr <- eval(r);
       
   561          if rr > 0 && rl % rr == 0) yield rl / rr          
       
   562 }
       
   563 
       
   564 eval(Add(Num(1), Num(2)))
       
   565 eval(Mul(Add(Num(1), Num(2)), Num(4)))
       
   566 eval(Sub(Num(3), Num(2)))
       
   567 eval(Sub(Num(3), Num(6)))
       
   568 eval(Div(Num(6), Num(2)))
       
   569 eval(Div(Num(6), Num(4)))
       
   570 
       
   571 def time_needed[T](n: Int, code: => T) = {
       
   572   val start = System.nanoTime()
       
   573   for (i <- (0 to n)) code
       
   574   val end = System.nanoTime()
       
   575   (end - start) / 1.0e9
       
   576 }
       
   577 
       
   578 def check(xs: Set[Int], target: Int) =
       
   579   search(xs).find(eval(_) == Some(target))
       
   580 
       
   581 for (sol <- check(Set(50, 5, 4, 9, 10, 8), 560)) {
       
   582   println(s"${pp(sol)} => ${eval(sol)}")
       
   583 }
       
   584 
       
   585 
       
   586 
       
   587 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560))
       
   588 
       
   589 
       
   590 println(check(Set(25, 5, 2, 10, 7, 1), 986).mkString("\n"))
       
   591 
       
   592 for (sol <- check(Set(25, 5, 2, 10, 7, 1), 986)) {
       
   593   println(s"${pp(sol)} => ${eval(sol)}")
       
   594 }
       
   595 
       
   596 for (sol <- check(Set(25, 5, 2, 10, 7, 1), -1)) {
       
   597   println(s"${pp(sol)} => ${eval(sol)}")
       
   598 }
       
   599 
       
   600 for (sol <- check(Set(100, 25, 75, 50, 7, 10), 360)) {
       
   601   println(s"${pp(sol)} => ${eval(sol)}")
       
   602 }
       
   603 time_needed(1, check(Set(100, 25, 75, 50, 7, 10), 360))
       
   604 
       
   605 
       
   606 
       
   607 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), 986))
       
   608 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), -1))
       
   609 
       
   610 
       
   611 def generate(nums: Set[Int]) : Set[(Tree, Int)] =  nums.size match {
       
   612   case 0 => Set()
       
   613   case 1 => Set((Num(nums.head), nums.head))
       
   614   case xs => {
       
   615     val spls = splits(nums)
       
   616     val subtrs =
       
   617       for ((lspls, rspls) <- spls;
       
   618            (lt, ln) <- generate(lspls); 
       
   619            (rt, rn) <- generate(rspls)) yield {
       
   620         Set((Add(lt, rt), ln + rn),
       
   621             (Mul(lt, rt), ln * rn))
       
   622         ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) 
       
   623         ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn))
       
   624         ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn))
       
   625         ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln))
       
   626     } 
       
   627     subtrs.flatten
       
   628   }
       
   629 }
       
   630 
       
   631 def check2(xs: Set[Int], target: Int) =
       
   632   generate(xs).find(_._2 == target)
       
   633 
       
   634 for ((sol, ev) <- check2(Set(50, 5, 4, 9, 10, 8), 560)) {
       
   635   println(s"${pp(sol)} => ${eval(sol)} / $ev")
       
   636 }
       
   637 
       
   638 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560))
       
   639 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), 560))
       
   640 
       
   641 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), -1))
       
   642 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), -1))
       
   643 
       
   644 
       
   645 // Sudoku 
       
   646 //========
       
   647 
       
   648 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
   649 // EFFICIENT AND FAST, just explaining exhaustive
       
   650 // depth-first search
       
   651 
       
   652 //> using dep org.scala-lang.modules::scala-parallel-collections:1.0.4
       
   653 import scala.collection.parallel.CollectionConverters.*
       
   654 
       
   655 
       
   656 val s1 = "s\n"
       
   657 val s2 = """s\n"""
       
   658 
       
   659 val game0 = """.14.6.3..
       
   660               |62...4..9
       
   661               |.8..5.6..
       
   662               |.6.2....3
       
   663               |.7..1..5.
       
   664               |5....9.6.
       
   665               |..6.2..3.
       
   666               |1..5...92
       
   667               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
   668 
       
   669 type Pos = (Int, Int)
       
   670 val EmptyValue = '.'
       
   671 val MaxValue = 9
       
   672 
       
   673 def pretty(game: String): String = 
       
   674   "\n" + (game.grouped(MaxValue).mkString("\n"))
       
   675 
       
   676 pretty(game0)
       
   677 
       
   678 
       
   679 val allValues = "123456789".toList
       
   680 val indexes = (0 to 8).toList
       
   681 
       
   682 def empty(game: String) = game.indexOf(EmptyValue)
       
   683 def isDone(game: String) = empty(game) == -1 
       
   684 def emptyPosition(game: String) : Pos = {
       
   685   val e = empty(game)
       
   686   (e % MaxValue, e / MaxValue)
       
   687 }
       
   688 
       
   689 def get_row(game: String, y: Int) = 
       
   690   indexes.map(col => game(y * MaxValue + col))
       
   691 def get_col(game: String, x: Int) = 
       
   692   indexes.map(row => game(x + row * MaxValue))
       
   693 
       
   694 //get_row(game0, 0)
       
   695 //get_row(game0, 1)
       
   696 //get_col(game0, 0)
       
   697 
       
   698 def get_box(game: String, pos: Pos): List[Char] = {
       
   699     def base(p: Int): Int = (p / 3) * 3
       
   700     val x0 = base(pos._1)
       
   701     val y0 = base(pos._2)
       
   702     val ys = (y0 until y0 + 3).toList
       
   703     (x0 until x0 + 3).toList
       
   704       .flatMap(x => ys.map(y => game(x + y * MaxValue)))
       
   705 }
       
   706 
       
   707 
       
   708 //get_box(game0, (3, 1))
       
   709 
       
   710 
       
   711 // this is not mutable!!
       
   712 def update(game: String, pos: Int, value: Char): String = 
       
   713   game.updated(pos, value)
       
   714 
       
   715 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   716   (get_col(game, pos._1) ++ 
       
   717    get_row(game, pos._2) ++ 
       
   718    get_box(game, pos))
       
   719 
       
   720 def candidates(game: String, pos: Pos): List[Char] = 
       
   721   allValues.diff(toAvoid(game, pos))
       
   722 
       
   723 //candidates(game0, (0,0))
       
   724 
       
   725 
       
   726 def search(game: String): List[String] = {
       
   727   if (isDone(game)) List(game)
       
   728   else {
       
   729     val cs = candidates(game, emptyPosition(game))
       
   730     cs.par.map(c => search(update(game, empty(game), c))).flatten.toList
       
   731   }
       
   732 }
       
   733 
       
   734 pretty(game0)
       
   735 search(game0).map(pretty)
       
   736 
       
   737 val game1 = """23.915...
       
   738               |...2..54.
       
   739               |6.7......
       
   740               |..1.....9
       
   741               |89.5.3.17
       
   742               |5.....6..
       
   743               |......9.5
       
   744               |.16..7...
       
   745               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   746 
       
   747 search(game1).map(pretty)
       
   748 
       
   749 // a game that is in the hard category
       
   750 val game2 = """8........
       
   751               |..36.....
       
   752               |.7..9.2..
       
   753               |.5...7...
       
   754               |....457..
       
   755               |...1...3.
       
   756               |..1....68
       
   757               |..85...1.
       
   758               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   759 
       
   760 search(game2).map(pretty)
       
   761 
       
   762 // game with multiple solutions
       
   763 val game3 = """.8...9743
       
   764               |.5...8.1.
       
   765               |.1.......
       
   766               |8....5...
       
   767               |...8.4...
       
   768               |...3....6
       
   769               |.......7.
       
   770               |.3.5...8.
       
   771               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   772 
       
   773 search(game3).map(pretty).foreach(println)
       
   774 
       
   775 // for measuring time
       
   776 def time_needed[T](i: Int, code: => T) = {
       
   777   val start = System.nanoTime()
       
   778   for (j <- 1 to i) code
       
   779   val end = System.nanoTime()
       
   780   s"${(end - start) / 1.0e9} secs"
       
   781 }
       
   782 
       
   783 time_needed(2, search(game2))
       
   784 
       
   785 
       
   786 // concurrency 
       
   787 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar 
       
   788 // import scala.collection.parallel.CollectionConverters._
       
   789 
       
   790 
       
   791 
       
   792 
       
   793 // String Interpolations
       
   794 //=======================
       
   795 
       
   796 def cube(n: Int) : Int = n * n * n
       
   797 
       
   798 val n = 3
       
   799 println("The cube of " + n + " is " + cube(n) + ".")
       
   800 
       
   801 println(s"The cube of $n is ${cube(n)}.")
       
   802 
       
   803 // or even
       
   804 
       
   805 println(s"The cube of $n is ${n * n * n}.")
       
   806 
       
   807 // helpful for debugging purposes
       
   808 //
       
   809 //     "The most effective debugging tool is still careful 
       
   810 //          thought, coupled with judiciously placed print 
       
   811 //                                             statements."
       
   812 //       — Brian W. Kernighan, in Unix for Beginners (1979)
       
   813 
       
   814 
       
   815 def gcd_db(a: Int, b: Int) : Int = {
       
   816   println(s"Function called with $a and $b.")
       
   817   if (b == 0) a else gcd_db(b, a % b)
       
   818 }
       
   819 
       
   820 gcd_db(48, 18)
       
   821 
       
   822 
       
   823 
       
   824 
       
   825 // Recursion Again ;o)
       
   826 //====================
       
   827 
       
   828 
       
   829 // another well-known example: Towers of Hanoi
       
   830 //=============================================
       
   831 
       
   832 def move(from: Char, to: Char) =
       
   833   println(s"Move disc from $from to $to!")
       
   834 
       
   835 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {
       
   836   if (n == 0) ()
       
   837   else {
       
   838     hanoi(n - 1, from, to, via)
       
   839     move(from, to)
       
   840     hanoi(n - 1, via, from, to)
       
   841   }
       
   842 } 
       
   843 
       
   844 hanoi(4, 'A', 'B', 'C')
       
   845 
       
   846 
       
   847 
       
   848 
       
   849 
   616 
   850 
   617 
   851 
   618 // Map type (upper-case)
   852 // Map type (upper-case)
   619 //=======================
   853 //=======================
   620 
   854