progs/lecture2.scala
changeset 318 029e2862bb4e
parent 317 607ceabeeffc
child 319 b84ea52bfd8f
equal deleted inserted replaced
317:607ceabeeffc 318:029e2862bb4e
    12 
    12 
    13 
    13 
    14 // String Interpolations
    14 // String Interpolations
    15 //=======================
    15 //=======================
    16 
    16 
       
    17 def cube(n: Int) : Int = n * n * n
       
    18 
    17 val n = 3
    19 val n = 3
    18 println("The square of " + n + " is " + square(n) + ".")
    20 println("The cube of " + n + " is " + cube(n) + ".")
    19 
    21 
    20 println(s"The square of ${n} is ${square(n)}.")
    22 println(s"The cube of ${n} is ${cube(n)}.")
    21 
    23 
       
    24 // or even
       
    25 
       
    26 println(s"The cube of ${n} is ${n * n * n}.")
    22 
    27 
    23 // helpful for debugging purposes
    28 // helpful for debugging purposes
    24 //
    29 //
    25 //         "The most effective debugging tool is still careful thought, 
    30 //         "The most effective debugging tool is still careful thought, 
    26 //          coupled with judiciously placed print statements."
    31 //          coupled with judiciously placed print statements."
    48 
    53 
    49 List(7,2,3,4,5,6).find(_ < 4)
    54 List(7,2,3,4,5,6).find(_ < 4)
    50 List(5,6,7,8,9).find(_ < 4)
    55 List(5,6,7,8,9).find(_ < 4)
    51 
    56 
    52 
    57 
    53 
       
    54 // better error handling with Options (no exceptions)
    58 // better error handling with Options (no exceptions)
    55 //
    59 //
    56 //  Try(something).getOrElse(what_to_do_in_case_of_an_exception)
    60 //  Try(something).getOrElse(what_to_do_in_case_of_an_exception)
    57 //
    61 //
    58 
    62 
    90 
    94 
    91 get_contents("text.txt")
    95 get_contents("text.txt")
    92 get_contents("test.txt")
    96 get_contents("test.txt")
    93 
    97 
    94 
    98 
    95 
       
    96 // operations on options
    99 // operations on options
    97 
   100 
    98 val lst = List(None, Some(1), Some(2), None, Some(3))
   101 val lst = List(None, Some(1), Some(2), None, Some(3))
    99 
   102 
   100 lst.flatten
   103 lst.flatten
   104 
   107 
   105 Some(1).isDefined
   108 Some(1).isDefined
   106 None.isDefined
   109 None.isDefined
   107 
   110 
   108 
   111 
   109 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 0), (1, 1))
   112 val ps = List((3, 0), (4, 2), (6, 2), (2, 0), (1, 0), (1, 1))
   110 
   113 
   111 // division where possible
   114 // division where possible
   112 
   115 
   113 for ((x, y) <- ps) yield {
   116 for ((x, y) <- ps) yield {
   114   if (y == 0) None else Some(x / y)
   117   if (y == 0) None else Some(x / y)
   119 val lst = List(None, Some(1), Some(2), None, Some(3))
   122 val lst = List(None, Some(1), Some(2), None, Some(3))
   120 
   123 
   121 for (x <- lst) yield x.getOrElse(0)
   124 for (x <- lst) yield x.getOrElse(0)
   122 
   125 
   123 
   126 
       
   127 // a function that turns strings into numbers (similar to .toInt)
       
   128 Integer.parseInt("1234")
       
   129 
       
   130 
       
   131 def get_me_an_int(s: String) : Option[Int] = 
       
   132  Try(Some(Integer.parseInt(s))).getOrElse(None)
   124 
   133 
   125 
   134 
   126 // This may not look any better than working with null in Java, but to
   135 // This may not look any better than working with null in Java, but to
   127 // see the value, you have to put yourself in the shoes of the
   136 // see the value, you have to put yourself in the shoes of the
   128 // consumer of the get_me_an_int function, and imagine you didn't
   137 // consumer of the get_me_an_int function, and imagine you didn't
   129 // write that function.
   138 // write that function.
   130 //
   139 //
   131 // In Java, if you didn't write this function, you'd have to depend on
   140 // In Java, if you didn't write this function, you'd have to depend on
   132 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, 
   141 // the Javadoc of the get_me_an_int. If you didn't look at the Javadoc, 
   133 // you might not know that get_me_an_int could return a null, and your 
   142 // you might not know that get_me_an_int could return null, and your 
   134 // code could potentially throw a NullPointerException.
   143 // code could potentially throw a NullPointerException.
   135 
       
   136 
   144 
   137 
   145 
   138 // even Scala is not immune to problems like this:
   146 // even Scala is not immune to problems like this:
   139 
   147 
   140 List(5,6,7,8,9).indexOf(7)
   148 List(5,6,7,8,9).indexOf(7)
   152 def even(x: Int) : Boolean = x % 2 == 0
   160 def even(x: Int) : Boolean = x % 2 == 0
   153 def odd(x: Int) : Boolean = x % 2 == 1
   161 def odd(x: Int) : Boolean = x % 2 == 1
   154 
   162 
   155 val lst = (1 to 10).toList
   163 val lst = (1 to 10).toList
   156 
   164 
   157 lst.filter(x => even(x))
       
   158 lst.filter(even(_))
       
   159 lst.filter(even)
   165 lst.filter(even)
   160 
       
   161 lst.count(even)
   166 lst.count(even)
   162 
       
   163 
       
   164 lst.find(even)
   167 lst.find(even)
   165 
   168 
   166 val ps = List((3, 0), (3, 2), (4, 2), (2, 2), (2, 0), (1, 1), (1, 0))
   169 lst.filter(x => x % 2 == 0)
       
   170 lst.filter(_ % 2 == 0)
   167 
   171 
   168 lst.sortWith(_ > _)
   172 lst.sortWith(_ > _)
   169 lst.sortWith(_ < _)
   173 lst.sortWith(_ < _)
   170 
   174 
       
   175 // but this only works when the arguments are clear, but 
       
   176 // not with multiple occurences
       
   177 lst.find(n => odd(n) && n > 2)
       
   178 
       
   179 
       
   180 val ps = List((3, 0), (3, 2), (4, 2), (2, 2), (2, 0), (1, 1), (1, 0))
       
   181 
   171 def lex(x: (Int, Int), y: (Int, Int)) : Boolean = 
   182 def lex(x: (Int, Int), y: (Int, Int)) : Boolean = 
   172   if (x._1 == y._1) x._2 < y._2 else x._1 < y._1
   183   if (x._1 == y._1) x._2 < y._2 else x._1 < y._1
   173 
   184 
   174 ps.sortWith(lex)
   185 ps.sortWith(lex)
   175 
   186 
   178 
   189 
   179 ps.maxBy(_._1)
   190 ps.maxBy(_._1)
   180 ps.maxBy(_._2)
   191 ps.maxBy(_._2)
   181 
   192 
   182 
   193 
   183 
       
   184 // maps (lower-case)
   194 // maps (lower-case)
   185 //===================
   195 //===================
   186 
   196 
   187 def double(x: Int): Int = x + x
   197 def double(x: Int): Int = x + x
   188 def square(x: Int): Int = x * x
   198 def square(x: Int): Int = x * x
   205 lst.map(square).filter(_ > 4)
   215 lst.map(square).filter(_ > 4)
   206 
   216 
   207 lst.map(square).filter(_ > 4).map(square)
   217 lst.map(square).filter(_ > 4).map(square)
   208 
   218 
   209 
   219 
   210 // lets define our own functions
   220 // lets define our own higher-order functions
   211 // type of functions, for example f: Int => Int
   221 // type of functions is for example Int => Int
   212 
   222 
   213 lst.tail
       
   214 
   223 
   215 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = {
   224 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = {
   216   if (lst == Nil) Nil
   225   if (lst == Nil) Nil
   217   else f(lst.head) :: my_map_int(lst.tail, f)
   226   else f(lst.head) :: my_map_int(lst.tail, f)
   218 }
   227 }
   245 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
   254 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst)
   246 
   255 
   247 sum_squares(lst)
   256 sum_squares(lst)
   248 sum_cubes(lst)
   257 sum_cubes(lst)
   249 
   258 
   250 // lets try it factorial
   259 // lets try a factorial
   251 def fact(n: Int) : Int = 
   260 def fact(n: Int) : Int = 
   252   if (n == 0) 1 else n * fact(n - 1)
   261   if (n == 0) 1 else n * fact(n - 1)
   253 
   262 
   254 def sum_fact(lst: List[Int]) = sumOf(fact, lst)
   263 def sum_fact(lst: List[Int]) = sumOf(fact, lst)
   255 sum_fact(lst)
   264 sum_fact(lst)
   256 
   265 
   257 
   266 
   258 
   267 
   259 // if you like verbosity, you can full-specify the literal. 
   268 // sometimes it is needed that you specify the type. 
   260 // Don't go telling that to people, though
       
   261 (1 to 100).filter((x: Int) => x % 2 == 0).sum 
   269 (1 to 100).filter((x: Int) => x % 2 == 0).sum 
   262 
   270 
   263 // As x is known to be an Int anyway, you can omit that part
   271 // in this case it is clear that x mist be an Int
   264 (1 to 100).filter(x => x % 2 == 0).sum
   272 (1 to 100).filter(x => x % 2 == 0).sum
   265 
   273 
   266 // As each parameter (only x in this case) is passed only once
   274 // As each parameter (only x in this case) is passed only once
   267 // you can use the wizardy placeholder syntax
   275 // you can use the wizardy placeholder syntax
   268 (1 to 100).filter(_ % 2 == 0).sum
   276 (1 to 100).filter(_ % 2 == 0).sum
   269 
   277 
   270 // But if you want to re-use your literal, you can also put it in a value
   278 
   271 // In this case, explicit types are required because there's nothing to infer from
   279 
   272 val isEven = (x: Int) => x % 2 == 0
   280 // Option Type and maps
   273 (1 to 100).filter(isEven).sum
   281 //======================
   274 
       
   275 
       
   276 
       
   277 // Option Type again
       
   278 //===================
       
   279 
   282 
   280 // a function that turns strings into numbers (similar to .toInt)
   283 // a function that turns strings into numbers (similar to .toInt)
   281 Integer.parseInt("12u34")
   284 Integer.parseInt("12u34")
   282 
   285 
       
   286 import scala.util._
   283 
   287 
   284 def get_me_an_int(s: String) : Option[Int] = 
   288 def get_me_an_int(s: String) : Option[Int] = 
   285  Try(Some(Integer.parseInt(s))).getOrElse(None)
   289  Try(Some(Integer.parseInt(s))).getOrElse(None)
   286 
   290 
   287 val lst = List("12345", "foo", "5432", "bar", "x21", "456")
   291 val lst = List("12345", "foo", "5432", "bar", "x21", "456")
   292 lst.map(get_me_an_int).flatten.sum
   296 lst.map(get_me_an_int).flatten.sum
   293 lst.map(get_me_an_int).flatten.sum
   297 lst.map(get_me_an_int).flatten.sum
   294 
   298 
   295 lst.flatMap(get_me_an_int).sum
   299 lst.flatMap(get_me_an_int).sum
   296 
   300 
       
   301 // maps on Options
       
   302 
       
   303 get_me_an_int("1234").map(even)
       
   304 get_me_an_int("12u34").map(even)
   297 
   305 
   298 
   306 
   299 
   307 
   300 // Map type (upper-case)
   308 // Map type (upper-case)
   301 //=======================
   309 //=======================
   302 
   310 
   303 // Note the difference between map and Map
   311 // Note the difference between map and Map
   304 
   312 
   305 def factors(n: Int) : List[Int] =
   313 def factors(n: Int) : List[Int] =
   306   ((1 until n).filter { divisor =>
   314   (2 until n).toList.filter(n % _ == 0)
   307       n % divisor == 0
       
   308     }).toList
       
   309 
       
   310 
   315 
   311 var ls = (1 to 10).toList
   316 var ls = (1 to 10).toList
   312 
       
   313 val facs = ls.map(n => (n, factors(n)))
   317 val facs = ls.map(n => (n, factors(n)))
   314 
   318 
   315 facs.find(_._1 == 4)
   319 facs.find(_._1 == 4)
   316 
   320 
   317 // works for lists of pairs
   321 // works for lists of pairs
   322 facs.toMap.getOrElse(42, Nil)
   326 facs.toMap.getOrElse(42, Nil)
   323 
   327 
   324 val facsMap = facs.toMap
   328 val facsMap = facs.toMap
   325 
   329 
   326 val facsMap0 = facsMap + (0 -> List(1,2,3,4,5))
   330 val facsMap0 = facsMap + (0 -> List(1,2,3,4,5))
   327 facsMap0.get(1)
   331 facsMap0.get(0)
   328 
   332 
   329 val facsMap4 = facsMap + (1 -> List(1,2,3,4,5))
   333 val facsMap2 = facsMap + (1 -> List(1,2,3,4,5))
   330 facsMap.get(1)
   334 facsMap.get(1)
   331 facsMap4.get(1)
   335 facsMap2.get(1)
       
   336 
       
   337 // groupBy function on maps
   332 
   338 
   333 val ls = List("one", "two", "three", "four", "five")
   339 val ls = List("one", "two", "three", "four", "five")
   334 ls.groupBy(_.length)
   340 ls.groupBy(_.length)
   335 
   341 
   336 ls.groupBy(_.length).get(2)
   342 ls.groupBy(_.length).get(3)
   337 
   343 
   338 
   344 
   339 
   345 
   340 
   346 
   341 // Pattern Matching
   347 // Pattern Matching
   362 
   368 
   363 
   369 
   364 def my_flatten(xs: List[Option[Int]]): List[Int] = xs match {
   370 def my_flatten(xs: List[Option[Int]]): List[Int] = xs match {
   365   case Nil => Nil 
   371   case Nil => Nil 
   366   case None::rest => my_flatten(rest)
   372   case None::rest => my_flatten(rest)
   367   case Some(v)::foo => {
   373   case Some(v)::rest => v :: my_flatten(rest)
   368       v :: my_flatten(foo)
   374 }
   369   } 
   375 
   370 }
   376 
   371 
   377 // another example with a default case
   372 
       
   373 // another example
       
   374 def get_me_a_string(n: Int): String = n match {
   378 def get_me_a_string(n: Int): String = n match {
   375   case 0 | 1 | 2 => "small"
   379   case 0 | 1 | 2 => "small"
   376   case _ => "big"
   380   case _ => "big"
   377 }
   381 }
   378 
   382 
   392 
   396 
   393 // What happens if no case matches?
   397 // What happens if no case matches?
   394 println(season("foobar"))
   398 println(season("foobar"))
   395 
   399 
   396 
   400 
   397 // Days of the months
   401 // days of some months
   398 def days(month: String) : Int = month match {
   402 def days(month: String) : Int = month match {
   399   case "March" | "April" | "May" => 31
   403   case "March" | "April" | "May" => 31
   400   case "June" | "July" | "August" => 30
   404   case "June" | "July" | "August" => 30
   401 }
   405 }
   402 
       
   403 
       
   404 
   406 
   405 
   407 
   406 // Silly: fizz buzz
   408 // Silly: fizz buzz
   407 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
   409 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
   408   case (0, 0) => "fizz buzz"
   410   case (0, 0) => "fizz buzz"
   413 
   415 
   414 for (n <- 0 to 20) 
   416 for (n <- 0 to 20) 
   415  println(fizz_buzz(n))
   417  println(fizz_buzz(n))
   416 
   418 
   417 
   419 
   418 // User-defined Datatypes
       
   419 //========================
       
   420 
       
   421 
       
   422 abstract class Colour
       
   423 case object Red extends Colour 
       
   424 case object Green extends Colour 
       
   425 case object Blue extends Colour
       
   426 
       
   427 def fav_colour(c: Colour) : Boolean = c match {
       
   428   case Red   => false
       
   429   case Green => true
       
   430   case Blue  => false 
       
   431 }
       
   432 
       
   433 fav_colour(Green)
       
   434 
       
   435 
       
   436 // ... a tiny bit more useful: Roman Numerals
       
   437 
       
   438 abstract class RomanDigit 
       
   439 case object I extends RomanDigit 
       
   440 case object V extends RomanDigit 
       
   441 case object X extends RomanDigit 
       
   442 case object L extends RomanDigit 
       
   443 case object C extends RomanDigit 
       
   444 case object D extends RomanDigit 
       
   445 case object M extends RomanDigit 
       
   446 
       
   447 type RomanNumeral = List[RomanDigit] 
       
   448 
       
   449 List(X,I)
       
   450 
       
   451 /*
       
   452 I -> 1
       
   453 II -> 2
       
   454 III  -> 3
       
   455 IV -> 4
       
   456 V -> 5
       
   457 VI -> 6
       
   458 VII -> 7
       
   459 VIII -> 8
       
   460 IX -> 9
       
   461 X -> X
       
   462 */
       
   463 
       
   464 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
       
   465   case Nil => 0
       
   466   case M::r    => 1000 + RomanNumeral2Int(r)  
       
   467   case C::M::r => 900 + RomanNumeral2Int(r)
       
   468   case D::r    => 500 + RomanNumeral2Int(r)
       
   469   case C::D::r => 400 + RomanNumeral2Int(r)
       
   470   case C::r    => 100 + RomanNumeral2Int(r)
       
   471   case X::C::r => 90 + RomanNumeral2Int(r)
       
   472   case L::r    => 50 + RomanNumeral2Int(r)
       
   473   case X::L::r => 40 + RomanNumeral2Int(r)
       
   474   case X::r    => 10 + RomanNumeral2Int(r)
       
   475   case I::X::r => 9 + RomanNumeral2Int(r)
       
   476   case V::r    => 5 + RomanNumeral2Int(r)
       
   477   case I::V::r => 4 + RomanNumeral2Int(r)
       
   478   case I::r    => 1 + RomanNumeral2Int(r)
       
   479 }
       
   480 
       
   481 RomanNumeral2Int(List(I,V))             // 4
       
   482 RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid Roman number)
       
   483 RomanNumeral2Int(List(V,I))             // 6
       
   484 RomanNumeral2Int(List(I,X))             // 9
       
   485 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
       
   486 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
       
   487 
       
   488 
       
   489 // another example
       
   490 //=================
       
   491 
       
   492 // Once upon a time, in a complete fictional 
       
   493 // country there were Persons...
       
   494 
       
   495 
       
   496 abstract class Person
       
   497 case object King extends Person
       
   498 case class Peer(deg: String, terr: String, succ: Int) extends Person
       
   499 case class Knight(name: String) extends Person
       
   500 case class Peasant(name: String) extends Person
       
   501 
       
   502 
       
   503 def title(p: Person): String = p match {
       
   504   case King => "His Majesty the King"
       
   505   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
       
   506   case Knight(name) => s"Sir ${name}"
       
   507   case Peasant(name) => name
       
   508 }
       
   509 
       
   510 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
       
   511   case (King, _) => true
       
   512   case (Peer(_,_,_), Knight(_)) => true
       
   513   case (Peer(_,_,_), Peasant(_)) => true
       
   514   case (Peer(_,_,_), Clown) => true
       
   515   case (Knight(_), Peasant(_)) => true
       
   516   case (Knight(_), Clown) => true
       
   517   case (Clown, Peasant(_)) => true
       
   518   case _ => false
       
   519 }
       
   520 
       
   521 val people = List(Knight("David"), 
       
   522                   Peer("Duke", "Norfolk", 84), 
       
   523                   Peasant("Christian"), 
       
   524                   King, 
       
   525                   Clown)
       
   526 
       
   527 println(people.sortWith(superior).mkString("\n"))
       
   528 
       
   529 
       
   530 // String interpolations as patterns
       
   531 
       
   532 val date = "2000-01-01"
       
   533 val s"$year-$month-$day" = date
       
   534 
       
   535 def parse_date(date: String) = date match {
       
   536   case s"$year-$month-$day" => Some((year.toInt, month.toInt, day.toInt))
       
   537   case s"$day/$month/$year" => Some((year.toInt, month.toInt, day.toInt))
       
   538   case _ => None
       
   539 } 
       
   540 
   420 
   541 
   421 
   542 // Recursion
   422 // Recursion
   543 //===========
   423 //===========
   544 
   424 
   545 /* a, b, c
   425 // well-known example
   546 
   426 
   547 aa         aaa
   427 def fib(n: Int) : Int = { 
   548 ab         baa 
   428   if (n == 0 || n == 1) 1
   549 ac         caa 
   429    else fib(n - 1) + fib(n - 2)
   550 ba  =>     ......
   430 }
   551 bb
   431 
   552 bc
   432 
   553 ca
   433 /* Say you have characters a, b, c.
   554 cb
   434    What are all the combinations of a certain length?
   555 cc
   435 
   556 
   436    All combinations of length 2:
       
   437   
       
   438      aa, ab, ac, ba, bb, bc, ca, cb, cc
       
   439 
       
   440    Combinations of length 3:
       
   441    
       
   442      aaa, baa, caa, and so on......
   557 */
   443 */
   558 
   444 
   559 def perms(cs: List[Char], l: Int) : List[String] = {
   445 def combs(cs: List[Char], l: Int) : List[String] = {
   560   if (l == 0) List("")
   446   if (l == 0) List("")
   561   else for (c <- cs; s <- perms(cs, l - 1)) yield s"$c$s"
   447   else for (c <- cs; s <- combs(cs, l - 1)) yield s"$c$s"
   562 }
   448 }
   563 
   449 
   564 perms("abc".toList, 2)
   450 combs("abc".toList, 2)
       
   451 
       
   452 
       
   453 // another well-known example
   565 
   454 
   566 def move(from: Char, to: Char) =
   455 def move(from: Char, to: Char) =
   567   println(s"Move disc from $from to $to!")
   456   println(s"Move disc from $from to $to!")
   568 
   457 
   569 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {
   458 def hanoi(n: Int, from: Char, via: Char, to: Char) : Unit = {
   573     move(from, to)
   462     move(from, to)
   574     hanoi(n - 1, via, from, to)
   463     hanoi(n - 1, via, from, to)
   575   }
   464   }
   576 } 
   465 } 
   577 
   466 
   578 hanoi(40, 'A', 'B', 'C')
   467 hanoi(4, 'A', 'B', 'C')
   579 
   468 
   580 
   469 
   581 // Tail Recursion
   470 // A Recursive Web Crawler / Email Harvester
   582 //================
   471 //===========================================
   583 
       
   584 
       
   585 def fact(n: Long): Long = 
       
   586   if (n == 0) 1 else n * fact(n - 1)
       
   587 
       
   588 fact(10)              //ok
       
   589 fact(10000)           // produces a stackoverflow
       
   590 
       
   591 def factT(n: BigInt, acc: BigInt): BigInt =
       
   592   if (n == 0) acc else factT(n - 1, n * acc)
       
   593 
       
   594 factT(10, 1)
       
   595 factT(100000, 1)
       
   596 
       
   597 // there is a flag for ensuring a function is tail recursive
       
   598 import scala.annotation.tailrec
       
   599 
       
   600 @tailrec
       
   601 def factT(n: BigInt, acc: BigInt): BigInt =
       
   602   if (n == 0) acc else factT(n - 1, n * acc)
       
   603 
       
   604 
       
   605 
       
   606 // for tail-recursive functions the Scala compiler
       
   607 // generates loop-like code, which does not need
       
   608 // to allocate stack-space in each recursive
       
   609 // call; Scala can do this only for tail-recursive
       
   610 // functions
       
   611 
       
   612 
       
   613 // A Web Crawler / Email Harvester
       
   614 //=================================
       
   615 //
   472 //
   616 // the idea is to look for links using the
   473 // the idea is to look for links using the
   617 // regular expression "https?://[^"]*" and for
   474 // regular expression "https?://[^"]*" and for
   618 // email addresses using another regex.
   475 // email addresses using another regex.
   619 
   476 
   641 def get_all_URLs(page: String): Set[String] = 
   498 def get_all_URLs(page: String): Set[String] = 
   642   http_pattern.findAllIn(page).map(unquote).toSet
   499   http_pattern.findAllIn(page).map(unquote).toSet
   643 
   500 
   644 // naive version of crawl - searches until a given depth,
   501 // naive version of crawl - searches until a given depth,
   645 // visits pages potentially more than once
   502 // visits pages potentially more than once
   646 def crawl(url: String, n: Int) : Set[String] = {
   503 def crawl(url: String, n: Int) : Unit = {
       
   504   if (n == 0) ()
       
   505   else {
       
   506     println(s"  Visiting: $n $url")
       
   507     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
       
   508   }
       
   509 }
       
   510 
       
   511 // some starting URLs for the crawler
       
   512 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
       
   513 
       
   514 crawl(startURL, 2)
       
   515 
       
   516 
       
   517 // a primitive email harvester
       
   518 def emails(url: String, n: Int) : Set[String] = {
   647   if (n == 0) Set()
   519   if (n == 0) Set()
   648   else {
   520   else {
   649     println(s"  Visiting: $n $url")
   521     println(s"  Visiting: $n $url")
   650     val page = get_page(url)
   522     val page = get_page(url)
   651     val new_emails = email_pattern.findAllIn(page).toSet
   523     val new_emails = email_pattern.findAllIn(page).toSet
   652     new_emails ++ (for (u <- get_all_URLs(page)) yield crawl(u, n - 1)).flatten
   524     new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten
   653   }
   525   }
   654 }
   526 }
   655 
   527 
   656 // some starting URLs for the crawler
   528 emails(startURL, 3)
   657 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
   529 
   658 
   530 
   659 crawl(startURL, 2)
   531 // if we want to explore the internet "deeper", then we
   660 
   532 // first have to parallelise the request of webpages:
   661 
   533 //
   662 
   534 // scala -cp scala-parallel-collections_2.13-0.2.0.jar 
   663 
   535 // import scala.collection.parallel.CollectionConverters._
   664 
   536 
   665 
   537 
   666 
   538 
   667 // Sudoku
   539 
   668 //========
   540 
   669 
   541 
   670 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
   671 // EFFICIENT AND FAST, just explaining exhaustive
       
   672 // depth-first search
       
   673 
       
   674 
       
   675 val game0 = """.14.6.3..
       
   676               |62...4..9
       
   677               |.8..5.6..
       
   678               |.6.2....3
       
   679               |.7..1..5.
       
   680               |5....9.6.
       
   681               |..6.2..3.
       
   682               |1..5...92
       
   683               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
   684 
       
   685 type Pos = (Int, Int)
       
   686 val emptyValue = '.'
       
   687 val maxValue = 9
       
   688 
       
   689 val allValues = "123456789".toList
       
   690 val indexes = (0 to 8).toList
       
   691 
       
   692 
       
   693 def empty(game: String) = game.indexOf(emptyValue)
       
   694 def isDone(game: String) = empty(game) == -1 
       
   695 def emptyPosition(game: String) : Pos = 
       
   696   (empty(game) % maxValue, empty(game) / maxValue)
       
   697 
       
   698 
       
   699 def get_row(game: String, y: Int) = indexes.map(col => game(y * maxValue + col))
       
   700 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * maxValue))
       
   701 
       
   702 def get_box(game: String, pos: Pos): List[Char] = {
       
   703     def base(p: Int): Int = (p / 3) * 3
       
   704     val x0 = base(pos._1)
       
   705     val y0 = base(pos._2)
       
   706     for (x <- (x0 until x0 + 3).toList;
       
   707          y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue)
       
   708 }         
       
   709 
       
   710 
       
   711 //get_row(game0, 0)
       
   712 //get_row(game0, 1)
       
   713 //get_box(game0, (3,1))
       
   714 
       
   715 def update(game: String, pos: Int, value: Char): String = 
       
   716   game.updated(pos, value)
       
   717 
       
   718 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   719   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
       
   720 
       
   721 def candidates(game: String, pos: Pos): List[Char] = 
       
   722   allValues.diff(toAvoid(game, pos))
       
   723 
       
   724 //candidates(game0, (0, 0))
       
   725 
       
   726 def pretty(game: String): String = 
       
   727   "\n" ++ (game.sliding(maxValue, maxValue).mkString("\n"))
       
   728 
       
   729 def search(game: String): List[String] = {
       
   730   if (isDone(game)) List(game)
       
   731   else 
       
   732     candidates(game, emptyPosition(game)).
       
   733       map(c => search(update(game, empty(game), c))).flatten
       
   734 }
       
   735 
       
   736 // an easy game
       
   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 
       
   748 // a game that is in the sligtly harder category
       
   749 val game2 = """8........
       
   750               |..36.....
       
   751               |.7..9.2..
       
   752               |.5...7...
       
   753               |....457..
       
   754               |...1...3.
       
   755               |..1....68
       
   756               |..85...1.
       
   757               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   758 
       
   759 // a game with multiple solutions
       
   760 val game3 = """.8...9743
       
   761               |.5...8.1.
       
   762               |.1.......
       
   763               |8....5...
       
   764               |...8.4...
       
   765               |...3....6
       
   766               |.......7.
       
   767               |.3.5...8.
       
   768               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   769 
       
   770 
       
   771 search(game0).map(pretty)
       
   772 search(game1).map(pretty)
       
   773 
       
   774 // for measuring time
       
   775 def time_needed[T](i: Int, code: => T) = {
       
   776   val start = System.nanoTime()
       
   777   for (j <- 1 to i) code
       
   778   val end = System.nanoTime()
       
   779   s"${(end - start) / i / 1.0e9} secs"
       
   780 }
       
   781 
       
   782 search(game2).map(pretty)
       
   783 search(game3).distinct.length
       
   784 time_needed(3, search(game2))
       
   785 time_needed(3, search(game3))
       
   786 
       
   787 
       
   788 
       
   789 
       
   790 
       
   791