progs/lecture2.scala
changeset 204 9b45dd24271b
parent 192 a112e0e2325c
child 212 4bda49ec24da
equal deleted inserted replaced
203:eb188f9ac038 204:9b45dd24271b
     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 
    41 }
   237 }
    42 
   238 
    43 // getOrElse is for setting a default value
   239 // getOrElse is for setting a default value
    44 
   240 
    45 val lst = List(None, Some(1), Some(2), None, Some(3))
   241 val lst = List(None, Some(1), Some(2), None, Some(3))
       
   242 
    46 for (x <- lst) yield x.getOrElse(0)
   243 for (x <- lst) yield x.getOrElse(0)
    47 
   244 
    48 
   245 
    49 
   246 
    50 
   247 
    59 
   256 
    60 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")
   257 Try(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString).getOrElse("")
    61 
   258 
    62 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)
   259 Try(Some(Source.fromURL("""http://www.inf.kcl.ac.uk/staff/urbanc/""").mkString)).getOrElse(None)
    63 
   260 
    64 // a function that turns strings into numbers
   261 
    65 Integer.parseInt("12u34")
   262 // a function that turns strings into numbers (similar to .toInt)
    66 
   263 Integer.parseInt("1234")
    67 def get_me_an_int(s: String): Option[Int] = 
   264 
       
   265 
       
   266 def get_me_an_int(s: String) : Option[Int] = 
    68  Try(Some(Integer.parseInt(s))).getOrElse(None)
   267  Try(Some(Integer.parseInt(s))).getOrElse(None)
    69 
   268 
    70 val lst = List("12345", "foo", "5432", "bar", "x21")
   269 val lst = List("12345", "foo", "5432", "bar", "x21", "456")
    71 for (x <- lst) yield get_me_an_int(x)
   270 for (x <- lst) yield get_me_an_int(x)
    72 
   271 
    73 // summing all the numbers
   272 // summing all the numbers
    74 val sum = lst.flatMap(get_me_an_int(_)).sum
   273 
       
   274 lst.map(get_me_an_int)
       
   275 lst.map(get_me_an_int).flatten.sum
       
   276 
       
   277 
       
   278 val sum = lst.flatMap(get_me_an_int).sum
    75 
   279 
    76 
   280 
    77 // This may not look any better than working with null in Java, but to
   281 // This may not look any better than working with null in Java, but to
    78 // see the value, you have to put yourself in the shoes of the
   282 // see the value, you have to put yourself in the shoes of the
    79 // consumer of the get_me_an_int function, and imagine you didn't
   283 // consumer of the get_me_an_int function, and imagine you didn't
    87 
   291 
    88 
   292 
    89 // even Scala is not immune to problems like this:
   293 // even Scala is not immune to problems like this:
    90 
   294 
    91 List(5,6,7,8,9).indexOf(7)
   295 List(5,6,7,8,9).indexOf(7)
    92 
   296 List(5,6,7,8,9).indexOf(10)
    93 
   297 
    94 
       
    95 
       
    96 
       
    97 // Type abbreviations
       
    98 //====================
       
    99 
       
   100 // some syntactic convenience
       
   101 type Pos = (int, Int)
       
   102 
       
   103 type Board = List[List[Int]]
       
   104 
       
   105 
       
   106 
       
   107 // Implicits
       
   108 //===========
       
   109 //
       
   110 // for example adding your own methods to Strings:
       
   111 // imagine you want to increment strings, like
       
   112 //
       
   113 //     "HAL".increment
       
   114 //
       
   115 // you can avoid ugly fudges, like a MyString, by
       
   116 // using implicit conversions
       
   117 
       
   118 
       
   119 implicit class MyString(s: String) {
       
   120   def increment = for (c <- s) yield (c + 1).toChar 
       
   121 }
       
   122 
       
   123 "HAL".increment
       
   124 
       
   125 
       
   126 // No return in Scala
       
   127 //====================
       
   128 
       
   129 //You should not use "return" in Scala:
       
   130 //
       
   131 // A return expression, when evaluated, abandons the 
       
   132 // current computation and returns to the caller of the 
       
   133 // function in which return appears."
       
   134 
       
   135 def sq1(x: Int): Int = x * x
       
   136 def sq2(x: Int): Int = return x * x
       
   137 
       
   138 def sumq(ls: List[Int]): Int = {
       
   139   (for (x <- ls) yield (return x * x)).sum[Int]
       
   140 }
       
   141 
       
   142 sumq(List(1,2,3,4))
       
   143 
       
   144 
       
   145 // last expression in a function is the return statement
       
   146 def square(x: Int): Int = {
       
   147   println(s"The argument is ${x}.")
       
   148   x * x
       
   149 }
       
   150 
   298 
   151 
   299 
   152 
   300 
   153 // Pattern Matching
   301 // Pattern Matching
   154 //==================
   302 //==================
   167 //    }
   315 //    }
   168 
   316 
   169 
   317 
   170 
   318 
   171 
   319 
   172 // remember
   320 // remember?
   173 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
   321 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
   174 
   322 
   175 
   323 
   176 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   324 def my_flatten(xs: List[Option[Int]]): List[Int] = {
   177   ...
   325   ...
   178 }
   326 }
       
   327 
   179 
   328 
   180 
   329 
   181 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
   330 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
   182   case Nil => Nil
   331   case Nil => Nil
   183   case None::xs => my_flatten(xs)
   332   case None::xs => my_flatten(xs)
   198 // you can also have cases combined
   347 // you can also have cases combined
   199 def season(month: String) = month match {
   348 def season(month: String) = month match {
   200   case "March" | "April" | "May" => "It's spring"
   349   case "March" | "April" | "May" => "It's spring"
   201   case "June" | "July" | "August" => "It's summer"
   350   case "June" | "July" | "August" => "It's summer"
   202   case "September" | "October" | "November" => "It's autumn"
   351   case "September" | "October" | "November" => "It's autumn"
   203   case "December" | "January" | "February" => "It's winter"
   352   case "December" => "It's winter"
       
   353   case "January" | "February" => "It's unfortunately winter"
   204 }
   354 }
   205  
   355  
   206 println(season("November"))
   356 println(season("November"))
   207 
   357 
   208 // What happens if no case matches?
   358 // What happens if no case matches?
   209 
   359 
   210 println(season("foobar"))
   360 println(season("foobar"))
   211 
   361 
   212 // fizz buzz
   362 // Silly: fizz buzz
   213 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
   363 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
   214   case (0, 0) => "fizz buzz"
   364   case (0, 0) => "fizz buzz"
   215   case (0, _) => "fizz"
   365   case (0, _) => "fizz"
   216   case (_, 0) => "buzz"
   366   case (_, 0) => "buzz"
   217   case _ => n.toString  
   367   case _ => n.toString  
   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 //========