|      1 // Scala Lecture 2 |      1 // Scala Lecture 2 | 
|      2 //================= |      2 //================= | 
|      3  |      3  | 
|         |      4 // UNFINISHED BUSINESS from Lecture 1 | 
|         |      5 //==================================== | 
|         |      6  | 
|         |      7  | 
|         |      8 // for measuring time | 
|         |      9 def time_needed[T](n: Int, code: => T) = { | 
|         |     10   val start = System.nanoTime() | 
|         |     11   for (i <- (0 to n)) code | 
|         |     12   val end = System.nanoTime() | 
|         |     13   (end - start) / 1.0e9 | 
|         |     14 } | 
|         |     15  | 
|         |     16  | 
|         |     17 val list = (1 to 1000000).toList | 
|         |     18 time_needed(10, for (n <- list) yield n + 42) | 
|         |     19 time_needed(10, for (n <- list.par) yield n + 42) | 
|         |     20  | 
|         |     21  | 
|         |     22 // Just for "Fun": Mutable vs Immutable | 
|         |     23 //======================================= | 
|         |     24 // | 
|         |     25 // - no vars, no ++i, no += | 
|         |     26 // - no mutable data-structures (no Arrays, no ListBuffers) | 
|         |     27  | 
|         |     28  | 
|         |     29 // Q: Count how many elements are in the intersections of two sets? | 
|         |     30  | 
|         |     31 def count_intersection(A: Set[Int], B: Set[Int]) : Int = { | 
|         |     32   var count = 0 | 
|         |     33   for (x <- A; if B contains x) count += 1  | 
|         |     34   count | 
|         |     35 } | 
|         |     36  | 
|         |     37 val A = (1 to 1000).toSet | 
|         |     38 val B = (1 to 1000 by 4).toSet | 
|         |     39  | 
|         |     40 count_intersection(A, B) | 
|         |     41  | 
|         |     42 // but do not try to add .par to the for-loop above | 
|         |     43  | 
|         |     44  | 
|         |     45 //propper parallel version | 
|         |     46 def count_intersection2(A: Set[Int], B: Set[Int]) : Int =  | 
|         |     47   A.par.count(x => B contains x) | 
|         |     48  | 
|         |     49 count_intersection2(A, B) | 
|         |     50  | 
|         |     51  | 
|         |     52 val A = (1 to 1000000).toSet | 
|         |     53 val B = (1 to 1000000 by 4).toSet | 
|         |     54  | 
|         |     55 time_needed(100, count_intersection(A, B)) | 
|         |     56 time_needed(100, count_intersection2(A, B)) | 
|         |     57  | 
|         |     58  | 
|         |     59  | 
|         |     60 // For-Comprehensions Again | 
|         |     61 //========================== | 
|         |     62  | 
|         |     63 // the first produces a result, while the second does not | 
|         |     64 for (n <- List(1, 2, 3, 4, 5)) yield n * n | 
|         |     65  | 
|         |     66  | 
|         |     67 for (n <- List(1, 2, 3, 4, 5)) println(n) | 
|         |     68  | 
|         |     69  | 
|         |     70  | 
|         |     71 // Higher-Order Functions | 
|         |     72 //======================== | 
|         |     73  | 
|         |     74 // functions can take functions as arguments | 
|         |     75  | 
|         |     76 def even(x: Int) : Boolean = x % 2 == 0 | 
|         |     77 def odd(x: Int) : Boolean = x % 2 == 1 | 
|         |     78  | 
|         |     79 val lst = (1 to 10).toList | 
|         |     80  | 
|         |     81 lst.filter(x => even(x)) | 
|         |     82 lst.filter(even(_)) | 
|         |     83 lst.filter(even) | 
|         |     84  | 
|         |     85 lst.count(even) | 
|         |     86  | 
|         |     87 lst.find(_ > 8) | 
|         |     88  | 
|         |     89  | 
|         |     90 val ps = List((3, 0), (3, 2), (4, 2), (2, 0), (1, 1), (1, 0)) | 
|         |     91  | 
|         |     92 ps.sortBy(_._1) | 
|         |     93 ps.sortBy(_._2) | 
|         |     94  | 
|         |     95 ps.maxBy(_._1) | 
|         |     96 ps.maxBy(_._2) | 
|         |     97  | 
|         |     98  | 
|         |     99  | 
|         |    100 // maps | 
|         |    101 //===== | 
|         |    102  | 
|         |    103 def square(x: Int): Int = x * x | 
|         |    104  | 
|         |    105 val lst = (1 to 10).toList | 
|         |    106  | 
|         |    107 lst.map(square) | 
|         |    108  | 
|         |    109 // this is actually what for is defined at in Scala | 
|         |    110  | 
|         |    111 lst.map(n => square(n)) | 
|         |    112 for (n <- lst) yield square(n) | 
|         |    113  | 
|         |    114 // this can be iterated | 
|         |    115  | 
|         |    116 lst.map(square).filter(_ > 4) | 
|         |    117  | 
|         |    118 lst.map(square).filter(_ > 4).map(square) | 
|         |    119  | 
|         |    120  | 
|         |    121 // lets define our own functions | 
|         |    122 // type of functions, for example f: Int => Int | 
|         |    123  | 
|         |    124 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = { | 
|         |    125   if (lst == Nil) Nil | 
|         |    126   else f(lst.head) :: my_map_int(lst.tail, f) | 
|         |    127 } | 
|         |    128  | 
|         |    129 my_map_int(lst, square) | 
|         |    130  | 
|         |    131  | 
|         |    132 // same function using pattern matching: a kind | 
|         |    133 // of switch statement on steroids (see more later on) | 
|         |    134  | 
|         |    135 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = lst match { | 
|         |    136   case Nil => Nil | 
|         |    137   case x::xs => f(x)::my_map_int(xs, f) | 
|         |    138 } | 
|         |    139  | 
|         |    140  | 
|         |    141 // other function types | 
|         |    142 // | 
|         |    143 // f1: (Int, Int) => Int | 
|         |    144 // f2: List[String] => Option[Int] | 
|         |    145 // ...  | 
|         |    146  | 
|         |    147  | 
|         |    148 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match { | 
|         |    149   case Nil => 0 | 
|         |    150   case x::xs => f(x) + sumOf(f, xs) | 
|         |    151 } | 
|         |    152  | 
|         |    153 def sum_squares(lst: List[Int]) = sumOf(square, lst) | 
|         |    154 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst) | 
|         |    155  | 
|         |    156 sum_squares(lst) | 
|         |    157 sum_cubes(lst) | 
|         |    158  | 
|         |    159 // lets try it factorial | 
|         |    160 def fact(n: Int) : Int = ... | 
|         |    161  | 
|         |    162 def sum_fact(lst: List[Int]) = sumOf(fact, lst) | 
|         |    163 sum_fact(lst) | 
|         |    164  | 
|         |    165  | 
|         |    166  | 
|         |    167  | 
|         |    168  | 
|         |    169 // Map type | 
|         |    170 //========== | 
|         |    171  | 
|         |    172 // Note the difference between map and Map | 
|         |    173  | 
|         |    174 def factors(n: Int) : List[Int] = | 
|         |    175   ((1 until n).filter { divisor => | 
|         |    176       n % divisor == 0 | 
|         |    177     }).toList | 
|         |    178  | 
|         |    179  | 
|         |    180 var ls = (1 to 10).toList | 
|         |    181  | 
|         |    182 val facs = ls.map(n => (n, factors(n))) | 
|         |    183  | 
|         |    184 facs.find(_._1 == 4) | 
|         |    185  | 
|         |    186 // works for lists of pairs | 
|         |    187 facs.toMap | 
|         |    188  | 
|         |    189  | 
|         |    190 facs.toMap.get(4) | 
|         |    191 facs.toMap.getOrElse(4, Nil) | 
|         |    192  | 
|         |    193 val facsMap = facs.toMap | 
|         |    194  | 
|         |    195 val facsMap0 = facsMap + (0 -> List(1,2,3,4,5)) | 
|         |    196 facsMap0.get(0) | 
|         |    197  | 
|         |    198 val facsMap4 = facsMap + (1 -> List(1,2,3,4,5)) | 
|         |    199 facsMap.get(1) | 
|         |    200 facsMap4.get(1) | 
|         |    201  | 
|         |    202 val ls = List("one", "two", "three", "four", "five") | 
|         |    203 ls.groupBy(_.length) | 
|         |    204  | 
|         |    205 ls.groupBy(_.length).get(3) | 
|         |    206  | 
|         |    207  | 
|      4  |    208  | 
|      5 // Option type |    209 // Option type | 
|      6 //============= |    210 //============= | 
|      7  |    211  | 
|      8 //in Java if something unusually happens, you return null; |    212 //in Java if something unusually happens, you return null; | 
|         |    213 // | 
|      9 //in Scala you use Option |    214 //in Scala you use Option | 
|     10 //   - if the value is present, you use Some(value) |    215 //   - if the value is present, you use Some(value) | 
|     11 //   - if no value is present, you use None |    216 //   - if no value is present, you use None | 
|     12  |    217  | 
|     13  |    218  | 
|     14 List(7,2,3,4,5,6).find(_ < 4) |    219 List(7,2,3,4,5,6).find(_ < 4) | 
|     15 List(5,6,7,8,9).find(_ < 4) |    220 List(5,6,7,8,9).find(_ < 4) | 
|     16  |    221  | 
|     17  |    222 // operations on options | 
|     18 // Values in types |         | 
|     19 // |         | 
|     20 // Boolean:  |         | 
|     21 // Int:  |         | 
|     22 // String:  |         | 
|     23 // |         | 
|     24 // Option[String]: |         | 
|     25 //    |         | 
|     26  |         | 
|     27  |    223  | 
|     28 val lst = List(None, Some(1), Some(2), None, Some(3)) |    224 val lst = List(None, Some(1), Some(2), None, Some(3)) | 
|     29  |    225  | 
|     30 lst.flatten |    226 lst.flatten | 
|     31  |    227  | 
|    222  |    372  | 
|    223  |    373  | 
|    224 // User-defined Datatypes |    374 // User-defined Datatypes | 
|    225 //======================== |    375 //======================== | 
|    226  |    376  | 
|    227 abstract class Tree |    377  | 
|    228 case class Node(elem: Int, left: Tree, right: Tree) extends Tree |    378 abstract class Colour | 
|    229 case class Leaf() extends Tree |    379 case object Red extends Colour  | 
|    230  |    380 case object Green extends Colour  | 
|    231  |    381 case object Blue extends Colour | 
|    232 def insert(tr: Tree, n: Int): Tree = tr match { |    382  | 
|    233   case Leaf() => Node(n, Leaf(), Leaf()) |    383 def fav_colour(c: Colour) : Boolean = c match { | 
|    234   case Node(m, left, right) =>  |    384   case Red   => false | 
|    235     if (n == m) Node(m, left, right)  |    385   case Green => true | 
|    236     else if (n < m) Node(m, insert(left, n), right) |    386   case Blue  => false  | 
|    237     else Node(m, left, insert(right, n)) |    387 } | 
|    238 } |    388  | 
|    239  |    389 fav_colour(Green) | 
|    240  |    390  | 
|    241 val t1 = Node(4, Node(2, Leaf(), Leaf()), Node(7, Leaf(), Leaf())) |    391  | 
|    242 insert(t1, 3) |    392 // ... a bit more useful: Roman Numerals | 
|    243  |    393  | 
|    244 def depth(tr: Tree): Int = tr match { |    394 abstract class RomanDigit  | 
|    245   case Leaf() => 0 |    395 case object I extends RomanDigit  | 
|    246   case Node(_, left, right) => 1 + List(depth(left), depth(right)).max |    396 case object V extends RomanDigit  | 
|    247 } |    397 case object X extends RomanDigit  | 
|    248  |    398 case object L extends RomanDigit  | 
|    249  |    399 case object C extends RomanDigit  | 
|    250 def balance(tr: Tree): Int = tr match { |    400 case object D extends RomanDigit  | 
|    251   case Leaf() => 0 |    401 case object M extends RomanDigit  | 
|    252   case Node(_, left, right) => depth(left) - depth(right) |    402  | 
|    253 } |    403 type RomanNumeral = List[RomanDigit]  | 
|    254  |    404  | 
|    255 balance(insert(t1, 3)) |    405 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match {  | 
|         |    406   case Nil => 0 | 
|         |    407   case M::r    => 1000 + RomanNumeral2Int(r)   | 
|         |    408   case C::M::r => 900 + RomanNumeral2Int(r) | 
|         |    409   case D::r    => 500 + RomanNumeral2Int(r) | 
|         |    410   case C::D::r => 400 + RomanNumeral2Int(r) | 
|         |    411   case C::r    => 100 + RomanNumeral2Int(r) | 
|         |    412   case X::C::r => 90 + RomanNumeral2Int(r) | 
|         |    413   case L::r    => 50 + RomanNumeral2Int(r) | 
|         |    414   case X::L::r => 40 + RomanNumeral2Int(r) | 
|         |    415   case X::r    => 10 + RomanNumeral2Int(r) | 
|         |    416   case I::X::r => 9 + RomanNumeral2Int(r) | 
|         |    417   case V::r    => 5 + RomanNumeral2Int(r) | 
|         |    418   case I::V::r => 4 + RomanNumeral2Int(r) | 
|         |    419   case I::r    => 1 + RomanNumeral2Int(r) | 
|         |    420 } | 
|         |    421  | 
|         |    422 RomanNumeral2Int(List(I,V))             // 4 | 
|         |    423 RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid Roman number) | 
|         |    424 RomanNumeral2Int(List(V,I))             // 6 | 
|         |    425 RomanNumeral2Int(List(I,X))             // 9 | 
|         |    426 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 | 
|         |    427 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017 | 
|         |    428  | 
|    256  |    429  | 
|    257 // another example |    430 // another example | 
|         |    431 //================= | 
|         |    432  | 
|         |    433 // Once upon a time, in a complete fictional country there were Persons... | 
|         |    434  | 
|    258  |    435  | 
|    259 abstract class Person |    436 abstract class Person | 
|    260 case class King() extends Person |    437 case object King extends Person | 
|    261 case class Peer(deg: String, terr: String, succ: Int) extends Person |    438 case class Peer(deg: String, terr: String, succ: Int) extends Person | 
|    262 case class Knight(name: String) extends Person |    439 case class Knight(name: String) extends Person | 
|    263 case class Peasant(name: String) extends Person |    440 case class Peasant(name: String) extends Person | 
|    264 case class Clown() extends Person |    441 case object Clown extends Person | 
|    265  |    442  | 
|    266 def title(p: Person): String = p match { |    443 def title(p: Person): String = p match { | 
|    267   case King() => "His Majesty the King" |    444   case King => "His Majesty the King" | 
|    268   case Peer(deg, terr, _) => s"The ${deg} of ${terr}" |    445   case Peer(deg, terr, _) => s"The ${deg} of ${terr}" | 
|    269   case Knight(name) => s"Sir ${name}" |    446   case Knight(name) => s"Sir ${name}" | 
|    270   case Peasant(name) => name |    447   case Peasant(name) => name | 
|    271 } |    448 } | 
|    272  |    449  | 
|    273 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { |    450 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match { | 
|    274   case (King(), _) => true |    451   case (King, _) => true | 
|    275   case (Peer(_,_,_), Knight(_)) => true |    452   case (Peer(_,_,_), Knight(_)) => true | 
|    276   case (Peer(_,_,_), Peasant(_)) => true |    453   case (Peer(_,_,_), Peasant(_)) => true | 
|    277   case (Peer(_,_,_), Clown()) => true |    454   case (Peer(_,_,_), Clown) => true | 
|    278   case (Knight(_), Peasant(_)) => true |    455   case (Knight(_), Peasant(_)) => true | 
|    279   case (Knight(_), Clown()) => true |    456   case (Knight(_), Clown) => true | 
|    280   case (Clown(), Peasant(_)) => true |    457   case (Clown, Peasant(_)) => true | 
|    281   case _ => false |    458   case _ => false | 
|    282 } |    459 } | 
|    283  |    460  | 
|    284 val people = List(Knight("David"),  |    461 val people = List(Knight("David"),  | 
|    285                   Peer("Duke", "Norfolk", 84),  |    462                   Peer("Duke", "Norfolk", 84),  | 
|    286                   Peasant("Christian"),  |    463                   Peasant("Christian"),  | 
|    287                   King(),  |    464                   King,  | 
|    288                   Clown()) |    465                   Clown) | 
|    289  |    466  | 
|    290 println(people.sortWith(superior(_, _)).mkString(", ")) |    467 println(people.sortWith(superior(_, _)).mkString(", ")) | 
|    291  |    468  | 
|    292  |    469  | 
|    293  |    470 // Tail recursion | 
|    294 // Higher-Order Functions |    471 //================ | 
|    295 //======================== |    472  | 
|    296  |    473  | 
|    297 // functions can take functions as arguments |    474 def fact(n: Long): Long =  | 
|    298  |    475   if (n == 0) 1 else n * fact(n - 1) | 
|    299 val lst = (1 to 10).toList |    476  | 
|    300  |    477 fact(10)              //ok | 
|    301 def even(x: Int): Boolean = x % 2 == 0 |    478 fact(10000)           // produces a stackoverflow | 
|    302 def odd(x: Int): Boolean = x % 2 == 1 |    479  | 
|    303  |    480 def factT(n: BigInt, acc: BigInt): BigInt = | 
|    304 lst.filter(x => even(x)) |    481   if (n == 0) acc else factT(n - 1, n * acc) | 
|    305 lst.filter(even(_)) |    482  | 
|    306 lst.filter(even) |    483 factT(10, 1) | 
|    307  |    484 factT(100000, 1) | 
|    308 lst.find(_ > 8) |    485  | 
|    309  |    486 // there is a flag for ensuring a function is tail recursive | 
|    310 def square(x: Int): Int = x * x |    487 import scala.annotation.tailrec | 
|    311  |    488  | 
|    312 lst.map(square) |    489 @tailrec | 
|    313  |    490 def factT(n: BigInt, acc: BigInt): BigInt = | 
|    314 lst.map(square).filter(_ > 4) |    491   if (n == 0) acc else factT(n - 1, n * acc) | 
|    315  |    492  | 
|    316 lst.map(square).filter(_ > 4).map(square) |    493  | 
|    317  |    494  | 
|    318 // in my collatz.scala |    495 // for tail-recursive functions the Scala compiler | 
|    319 //(1 to bnd).map(i => (collatz(i), i)).maxBy(_._1) |    496 // generates loop-like code, which does not need | 
|    320  |    497 // to allocate stack-space in each recursive | 
|    321  |    498 // call; Scala can do this only for tail-recursive | 
|    322 // type of functions, for example f: Int => Int |    499 // functions | 
|    323  |    500  | 
|    324 def my_map_int(lst: List[Int], f: Int => Int): List[Int] = lst match { |    501  | 
|    325   case Nil => Nil |    502 // A Web Crawler  | 
|    326   case x::xs => f(x)::my_map_int(xs, f) |    503 //=============== | 
|    327 } |    504 // | 
|    328  |    505 // the idea is to look for dead links using the | 
|    329 my_map_int(lst, square) |    506 // regular expression "https?://[^"]*" | 
|    330  |    507  | 
|    331 // other function types |    508 import io.Source | 
|    332 // |    509 import scala.util._ | 
|    333 // f1: (Int, Int) => Int |    510  | 
|    334 // f2: List[String] => Option[Int] |    511 // gets the first 10K of a web-page | 
|    335 // ...  |    512 def get_page(url: String) : String = { | 
|    336  |    513   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString). | 
|    337  |    514     getOrElse { println(s"  Problem with: $url"); ""} | 
|    338 def sumOf(f: Int => Int, lst: List[Int]): Int = lst match { |    515 } | 
|    339   case Nil => 0 |    516  | 
|    340   case x::xs => f(x) + sumOf(f, xs) |    517 // regex for URLs and emails | 
|    341 } |    518 val http_pattern = """"https?://[^"]*"""".r | 
|    342  |    519 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r | 
|    343 def sum_squares(lst: List[Int]) = sumOf(square, lst) |    520  | 
|    344 def sum_cubes(lst: List[Int])   = sumOf(x => x * x * x, lst) |    521  | 
|    345  |    522 // drops the first and last character from a string | 
|    346 sum_squares(lst) |    523 def unquote(s: String) = s.drop(1).dropRight(1) | 
|    347 sum_cubes(lst) |    524  | 
|    348  |    525 def get_all_URLs(page: String): Set[String] =  | 
|    349 // lets try it factorial |    526   http_pattern.findAllIn(page).map(unquote).toSet | 
|    350 def fact(n: Int): Int = ... |    527  | 
|    351  |    528 // naive version of crawl - searches until a given depth, | 
|    352 def sum_fact(lst: List[Int]) = sumOf(fact, lst) |    529 // visits pages potentially more than once | 
|    353 sum_fact(lst) |    530 def crawl(url: String, n: Int) : Set[String] = { | 
|    354  |    531   if (n == 0) Set() | 
|    355 // Avoid being mutable |    532   else { | 
|    356 //===================== |    533     println(s"  Visiting: $n $url") | 
|    357  |    534     val page = get_page(url) | 
|    358 // a student showed me... |    535     val new_emails = email_pattern.findAllIn(page).toSet | 
|    359 import scala.collection.mutable.ListBuffer |    536     new_emails ++ (for (u <- get_all_URLs(page).par) yield crawl(u, n - 1)).flatten | 
|    360  |    537   } | 
|    361  |    538 } | 
|    362  |    539  | 
|    363 def collatz_max(bnd: Long): (Long, Long) = { |    540 // some starting URLs for the crawler | 
|    364   val colNos = ListBuffer[(Long, Long)]() |    541 val startURL = """https://nms.kcl.ac.uk/christian.urban/""" | 
|    365   for (i <- (1L to bnd).toList) colNos += ((collatz(i), i)) |    542  | 
|    366   colNos.max |    543 crawl(startURL, 2) | 
|    367 } |    544  | 
|    368  |    545  | 
|    369 def collatz_max(bnd: Long): (Long, Long) = { |    546  | 
|    370   (1L to bnd).map((i) => (collatz(i), i)).maxBy(_._1) |    547  | 
|    371 } |         | 
|    372  |         | 
|    373 //views -> lazy collection |         | 
|    374 def collatz_max(bnd: Long): (Long, Long) = { |         | 
|    375   (1L to bnd).view.map((i) => (collatz(i), i)).maxBy(_._1) |         | 
|    376 } |         | 
|    377  |         | 
|    378 // raises a GC exception |         | 
|    379 (1 to 1000000000).filter(_ % 2 == 0).take(10).toList |         | 
|    380 // ==> java.lang.OutOfMemoryError: GC overhead limit exceeded |         | 
|    381  |         | 
|    382 (1 to 1000000000).view.filter(_ % 2 == 0).take(10).toList |         | 
|    383  |    548  | 
|    384  |    549  | 
|    385  |    550  | 
|    386 // Sudoku |    551 // Sudoku | 
|    387 //======== |    552 //======== |