progs/lecture3.scala
changeset 481 e03a0100ec46
parent 449 d67c5f7177a6
child 493 244df77507c2
equal deleted inserted replaced
480:a623dd1f2898 481:e03a0100ec46
     1 // Scala Lecture 3
     1 // Scala Lecture 3
     2 //=================
     2 //=================
     3 
     3 
     4 // - Higher-Order functions
     4 // last week:
     5 // - maps (behind for-comprehensions)
     5 // higher-order functions
     6 
     6 // maps
       
     7 
       
     8 // - recursion
       
     9 // - Sudoku
       
    10 // - string interpolations
     7 // - Pattern-Matching
    11 // - Pattern-Matching
     8 
    12 
     9 def fib(n: Int) : Int = n match {
    13 // A Recursive Web Crawler / Email Harvester
    10   case 0 => 1
    14 //===========================================
    11   case 1 =>  1
    15 //
    12   case n => fib(n - 1) + fib(n - 2)
    16 // the idea is to look for links using the
    13 }
    17 // regular expression "https?://[^"]*" and for
    14 
    18 // email addresses using another regex.
    15 
    19 
    16 abstract class Rexp
    20 import io.Source
    17 case object ZERO extends Rexp                      // matches nothing
    21 import scala.util._
    18 case object ONE extends Rexp                       // matches the empty string
    22 
    19 case class CHAR(c: Char) extends Rexp              // matches a character c
    23 // gets the first 10K of a web-page
    20 case class ALT(r1: Rexp, r2: Rexp) extends Rexp    // alternative
    24 def get_page(url: String) : String = {
    21 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp    // sequence
    25   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
    22 case class STAR(r: Rexp) extends Rexp              // star
    26     getOrElse { println(s"  Problem with: $url"); ""}
    23 
    27 }
    24 def depth(r: Rexp) : Int = r match {
    28 
    25   case ZERO => 1
    29 // regex for URLs and emails
    26   case ONE => 1
    30 val http_pattern = """"https?://[^"]*"""".r
    27   case CHAR(_) => 1
    31 val email_pattern = """([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{2,6})""".r
    28   case ALT(r1, r2) => 1 + List(depth(r1), depth(r2)).max
    32 
    29   case SEQ(r1, r2) => 1 + List(depth(r1), depth(r2)).max
    33 //test case:
    30   case STAR(r1) => 1 + depth(r1)
    34 //email_pattern.findAllIn
    31 }
    35 //  ("foo bla christian@kcl.ac.uk 1234567").toList
    32 
    36 
    33 
    37 
    34 // - String-Interpolations
    38 // drops the first and last character from a string
       
    39 def unquote(s: String) = s.drop(1).dropRight(1)
       
    40 
       
    41 def get_all_URLs(page: String): Set[String] = 
       
    42   http_pattern.findAllIn(page).map(unquote).toSet
       
    43 
       
    44 // naive version of crawl - searches until a given depth,
       
    45 // visits pages potentially more than once
       
    46 def crawl(url: String, n: Int) : Unit = {
       
    47   if (n == 0) ()
       
    48   else {
       
    49     println(s"  Visiting: $n $url")
       
    50     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
       
    51   }
       
    52 }
       
    53 
       
    54 // some starting URLs for the crawler
       
    55 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
       
    56 
       
    57 crawl(startURL, 2)
       
    58 
       
    59 
       
    60 // a primitive email harvester
       
    61 def emails(url: String, n: Int) : Set[String] = {
       
    62   if (n == 0) Set()
       
    63   else {
       
    64     println(s"  Visiting: $n $url")
       
    65     val page = get_page(url)
       
    66     val new_emails = email_pattern.findAllIn(page).toSet
       
    67     new_emails ++ (for (u <- get_all_URLs(page)) yield emails(u, n - 1)).flatten
       
    68   }
       
    69 }
       
    70 
       
    71 emails(startURL, 2)
       
    72 
       
    73 
       
    74 
       
    75 // Sudoku 
       
    76 //========
       
    77 
       
    78 // THE POINT OF THIS CODE IS NOT TO BE SUPER
       
    79 // EFFICIENT AND FAST, just explaining exhaustive
       
    80 // depth-first search
       
    81 
       
    82 
       
    83 val game0 = """.14.6.3..
       
    84               |62...4..9
       
    85               |.8..5.6..
       
    86               |.6.2....3
       
    87               |.7..1..5.
       
    88               |5....9.6.
       
    89               |..6.2..3.
       
    90               |1..5...92
       
    91               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
       
    92 
       
    93 type Pos = (Int, Int)
       
    94 val EmptyValue = '.'
       
    95 val MaxValue = 9
       
    96 
       
    97 def pretty(game: String): String = 
       
    98   "\n" + (game.grouped(MaxValue).mkString("\n"))
       
    99 
       
   100 pretty(game0)
       
   101 
       
   102 
       
   103 val allValues = "123456789".toList
       
   104 val indexes = (0 to 8).toList
       
   105 
       
   106 def empty(game: String) = game.indexOf(EmptyValue)
       
   107 def isDone(game: String) = empty(game) == -1 
       
   108 def emptyPosition(game: String) : Pos = {
       
   109   val e = empty(game)
       
   110   (e % MaxValue, e / MaxValue)
       
   111 }
       
   112 
       
   113 def get_row(game: String, y: Int) = 
       
   114   indexes.map(col => game(y * MaxValue + col))
       
   115 def get_col(game: String, x: Int) = 
       
   116   indexes.map(row => game(x + row * MaxValue))
       
   117 
       
   118 //get_row(game0, 0)
       
   119 //get_row(game0, 1)
       
   120 //get_col(game0, 0)
       
   121 
       
   122 def get_box(game: String, pos: Pos): List[Char] = {
       
   123     def base(p: Int): Int = (p / 3) * 3
       
   124     val x0 = base(pos._1)
       
   125     val y0 = base(pos._2)
       
   126     val ys = (y0 until y0 + 3).toList
       
   127     (x0 until x0 + 3).toList
       
   128       .flatMap(x => ys.map(y => game(x + y * MaxValue)))
       
   129 }
       
   130 
       
   131 
       
   132 //get_box(game0, (3, 1))
       
   133 
       
   134 
       
   135 // this is not mutable!!
       
   136 def update(game: String, pos: Int, value: Char): String = 
       
   137   game.updated(pos, value)
       
   138 
       
   139 def toAvoid(game: String, pos: Pos): List[Char] = 
       
   140   (get_col(game, pos._1) ++ 
       
   141    get_row(game, pos._2) ++ 
       
   142    get_box(game, pos))
       
   143 
       
   144 def candidates(game: String, pos: Pos): List[Char] = 
       
   145   allValues.diff(toAvoid(game, pos))
       
   146 
       
   147 //candidates(game0, (0,0))
       
   148 
       
   149 
       
   150 def search(game: String): List[String] = {
       
   151   if (isDone(game)) List(game)
       
   152   else {
       
   153     val cs = candidates(game, emptyPosition(game))
       
   154     cs.par.map(c => search(update(game, empty(game), c))).flatten.toList
       
   155   }
       
   156 }
       
   157 
       
   158 pretty(game0)
       
   159 search(game0).map(pretty)
       
   160 
       
   161 val game1 = """23.915...
       
   162               |...2..54.
       
   163               |6.7......
       
   164               |..1.....9
       
   165               |89.5.3.17
       
   166               |5.....6..
       
   167               |......9.5
       
   168               |.16..7...
       
   169               |...329..1""".stripMargin.replaceAll("\\n", "")
       
   170 
       
   171 search(game1).map(pretty)
       
   172 
       
   173 // a game that is in the hard category
       
   174 val game2 = """8........
       
   175               |..36.....
       
   176               |.7..9.2..
       
   177               |.5...7...
       
   178               |....457..
       
   179               |...1...3.
       
   180               |..1....68
       
   181               |..85...1.
       
   182               |.9....4..""".stripMargin.replaceAll("\\n", "")
       
   183 
       
   184 search(game2).map(pretty)
       
   185 
       
   186 // game with multiple solutions
       
   187 val game3 = """.8...9743
       
   188               |.5...8.1.
       
   189               |.1.......
       
   190               |8....5...
       
   191               |...8.4...
       
   192               |...3....6
       
   193               |.......7.
       
   194               |.3.5...8.
       
   195               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   196 
       
   197 search(game3).map(pretty).foreach(println)
       
   198 
       
   199 // for measuring time
       
   200 def time_needed[T](i: Int, code: => T) = {
       
   201   val start = System.nanoTime()
       
   202   for (j <- 1 to i) code
       
   203   val end = System.nanoTime()
       
   204   s"${(end - start) / 1.0e9} secs"
       
   205 }
       
   206 
       
   207 time_needed(2, search(game2))
       
   208 
       
   209 
       
   210 // concurrency 
       
   211 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar 
       
   212 // import scala.collection.parallel.CollectionConverters._
       
   213 
       
   214 
       
   215 
    35 
   216 
    36 // String Interpolations
   217 // String Interpolations
    37 //=======================
   218 //=======================
    38 
   219 
    39 def cube(n: Int) : Int = n * n * n
   220 def cube(n: Int) : Int = n * n * n
    61 }
   242 }
    62 
   243 
    63 gcd_db(48, 18)
   244 gcd_db(48, 18)
    64 
   245 
    65 
   246 
    66 // naive quicksort with "On" function
       
    67 
       
    68 def sortOn(f: Int => Int, xs: List[Int]) : List[Int] = {
       
    69   if (xs.size < 2) xs
       
    70   else {
       
    71    val pivot = xs.head
       
    72    val (left, right) = xs.partition(f(_) < f(pivot))
       
    73    sortOn(f, left) ::: pivot :: sortOn(f, right.tail)
       
    74   }
       
    75 } 
       
    76 
       
    77 sortOn(identity, List(99,99,99,98,10,-3,2)) 
       
    78 sortOn(n => - n, List(99,99,99,98,10,-3,2))
       
    79 
   247 
    80 
   248 
    81 // Recursion Again ;o)
   249 // Recursion Again ;o)
    82 //====================
   250 //====================
    83 
   251 
    99 
   267 
   100 hanoi(4, 'A', 'B', 'C')
   268 hanoi(4, 'A', 'B', 'C')
   101 
   269 
   102 
   270 
   103 
   271 
   104 // User-defined Datatypes
   272 // Pattern Matching
   105 //========================
   273 //==================
       
   274 
       
   275 // A powerful tool which has even landed in Java during 
       
   276 // the last few years (https://inside.java/2021/06/13/podcast-017/).
       
   277 // ...Scala already has it for many years and the concept is
       
   278 // older than your friendly lecturer, that is stone old  ;o)
       
   279 
       
   280 // The general schema:
       
   281 //
       
   282 //    expression match {
       
   283 //       case pattern1 => expression1
       
   284 //       case pattern2 => expression2
       
   285 //       ...
       
   286 //       case patternN => expressionN
       
   287 //    }
       
   288 
       
   289 
       
   290 // recall
       
   291 def len(xs: List[Int]) : Int = {
       
   292     if (xs == Nil) 0
       
   293     else 1 + len(xs.tail)
       
   294 }    
       
   295 
       
   296 def len(xs: List[Int]) : Int = xs match {
       
   297     case Nil => 0
       
   298     case hd::tail => 1 + len(tail)
       
   299 }  
       
   300 
       
   301 
       
   302 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = 
       
   303   lst match {
       
   304     case Nil => Nil
       
   305     case x::xs => f(x)::my_map_int(xs, f)
       
   306   }
       
   307 
       
   308 def my_map_option(opt: Option[Int], f: Int => Int) : Option[Int] = 
       
   309   opt match {
       
   310     case None => None
       
   311     case Some(x) => Some(f(x))
       
   312   }
       
   313 
       
   314 my_map_option(None, x => x * x)
       
   315 my_map_option(Some(8), x => x * x)
       
   316 
       
   317 
       
   318 // you can also have cases combined
       
   319 def season(month: String) : String = month match {
       
   320   case "March" | "April" | "May" => "It's spring"
       
   321   case "June" | "July" | "August" => "It's summer"
       
   322   case "September" | "October" | "November" => "It's autumn"
       
   323   case "December" => "It's winter"
       
   324   case "January" | "February" => "It's unfortunately winter"
       
   325   case _ => "Wrong month"
       
   326 }
       
   327 
       
   328 // pattern-match on integers
       
   329 
       
   330 def fib(n: Int) : Int = n match { 
       
   331   case 0 | 1 => 1
       
   332   case n => fib(n - 1) + fib(n - 2)
       
   333 }
       
   334 
       
   335 fib(10)
       
   336 
       
   337 // pattern-match on results
       
   338 
       
   339 // Silly: fizz buzz
       
   340 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
       
   341   case (0, 0) => "fizz buzz"
       
   342   case (0, _) => "fizz"
       
   343   case (_, 0) => "buzz"
       
   344   case _ => n.toString  
       
   345 }
       
   346 
       
   347 for (n <- 1 to 20) 
       
   348  println(fizz_buzz(n))
       
   349 
       
   350 // guards in pattern-matching
       
   351 
       
   352 def foo(xs: List[Int]) : String = xs match {
       
   353   case Nil => s"this list is empty"
       
   354   case x :: xs if x % 2 == 0 
       
   355      => s"the first elemnt is even"
       
   356   case x :: y :: rest if x == y
       
   357      => s"this has two elemnts that are the same"
       
   358   case hd :: tl => s"this list is standard $hd::$tl"
       
   359 }
       
   360 
       
   361 foo(Nil)
       
   362 foo(List(1,2,3))
       
   363 foo(List(1,2))
       
   364 foo(List(1,1,2,3))
       
   365 foo(List(2,2,2,3))
       
   366 
       
   367 
       
   368 // Trees
   106 
   369 
   107 abstract class Tree
   370 abstract class Tree
   108 case class Leaf(x: Int) extends Tree
   371 case class Leaf(x: Int) extends Tree
   109 case class Node(s: String, left: Tree, right: Tree) extends Tree 
   372 case class Node(s: String, left: Tree, right: Tree) extends Tree 
   110 
   373 
   180 RomanNumeral2Int(List(I,X))             // 9
   443 RomanNumeral2Int(List(I,X))             // 9
   181 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   444 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   182 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   445 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   183 
   446 
   184 
   447 
       
   448 abstract class Rexp
       
   449 case object ZERO extends Rexp                      // matches nothing
       
   450 case object ONE extends Rexp                       // matches the empty string
       
   451 case class CHAR(c: Char) extends Rexp              // matches a character c
       
   452 case class ALT(r1: Rexp, r2: Rexp) extends Rexp    // alternative
       
   453 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp    // sequence
       
   454 case class STAR(r: Rexp) extends Rexp              // star
       
   455 
       
   456 def depth(r: Rexp) : Int = r match {
       
   457   case ZERO => 1
       
   458   case ONE => 1
       
   459   case CHAR(_) => 1
       
   460   case ALT(r1, r2) => 1 + List(depth(r1), depth(r2)).max
       
   461   case SEQ(r1, r2) => 1 + List(depth(r1), depth(r2)).max
       
   462   case STAR(r1) => 1 + depth(r1)
       
   463 }
       
   464 
       
   465 
       
   466 
       
   467 
       
   468 
   185 // expressions (essentially trees)
   469 // expressions (essentially trees)
   186 
   470 
   187 abstract class Exp
   471 abstract class Exp
   188 case class N(n: Int) extends Exp                  // for numbers
   472 case class N(n: Int) extends Exp                  // for numbers
   189 case class Plus(e1: Exp, e2: Exp) extends Exp
   473 case class Plus(e1: Exp, e2: Exp) extends Exp
   252 parse_date("2019-11-26")
   536 parse_date("2019-11-26")
   253 parse_date("26/11/2019")
   537 parse_date("26/11/2019")
   254 parse_date("26.11.2019")
   538 parse_date("26.11.2019")
   255 
   539 
   256 
   540 
   257 // guards in pattern-matching
   541 
   258 
   542 
   259 def foo(xs: List[Int]) : String = xs match {
   543 // Map type (upper-case)
   260   case Nil => s"this list is empty"
   544 //=======================
   261   case x :: xs if x % 2 == 0
   545 
   262      => s"the first elemnt is even"
   546 // Note the difference between map and Map
   263   case x :: y :: rest if x == y
   547 
   264      => s"this has two elemnts that are the same"
   548 val m = Map(1 -> "one", 2 -> "two", 10 -> "many")
   265   case hd :: tl => s"this list is standard $hd::$tl"
   549 
   266 }
   550 List((1, "one"), (2, "two"), (10, "many")).toMap
   267 
   551 
   268 foo(Nil)
   552 m.get(1)
   269 foo(List(1,2,3))
   553 m.get(4)
   270 foo(List(1,2))
   554 
   271 foo(List(1,1,2,3))
   555 m.getOrElse(1, "")
   272 foo(List(2,2,2,3))
   556 m.getOrElse(4, "")
       
   557 
       
   558 val new_m = m + (10 -> "ten")
       
   559 
       
   560 new_m.get(10)
       
   561 
       
   562 val m2 = for ((k, v) <- m) yield (k, v.toUpperCase)
       
   563 
       
   564 
       
   565 
       
   566 // groupBy function on Maps
       
   567 val lst = List("one", "two", "three", "four", "five")
       
   568 lst.groupBy(_.head)
       
   569 
       
   570 lst.groupBy(_.length)
       
   571 
       
   572 lst.groupBy(_.length).get(3)
       
   573 
       
   574 val grps = lst.groupBy(_.length)
       
   575 grps.keySet
       
   576 
       
   577 
       
   578 
   273 
   579 
   274 // Tail recursion
   580 // Tail recursion
   275 //================
   581 //================
   276 
   582 
   277 def fact(n: BigInt): BigInt = 
   583 def fact(n: BigInt): BigInt = 
   314 }
   620 }
   315 
   621 
   316 lengthT(List.fill(10000000)(1), 0)
   622 lengthT(List.fill(10000000)(1), 0)
   317 
   623 
   318 
   624 
   319 // Sudoku
   625 
   320 //========
   626 
   321 
   627 
   322 // uses Strings for games
   628 
   323 
   629 
   324 type Pos = (Int, Int)
   630 // Aside: concurrency 
   325 val emptyValue = '.'
   631 // scala-cli --extra-jars scala-parallel-collections_3-1.0.4.jar 
   326 val maxValue = 9
   632 
   327 
   633 for (n <- (1 to 10)) println(n)
   328 val allValues = "123456789".toList
   634 
   329 val indexes = (0 to 8).toList
   635 import scala.collection.parallel.CollectionConverters._
   330 
   636 
   331 
   637 for (n <- (1 to 10).par) println(n)
   332 def empty(game: String) = game.indexOf(emptyValue)
   638 
   333 def isDone(game: String) = empty(game) == -1 
   639 
   334 def emptyPosition(game: String) : Pos = 
   640 // for measuring time
   335   (empty(game) % maxValue, empty(game) / maxValue)
   641 def time_needed[T](n: Int, code: => T) = {
   336 
   642   val start = System.nanoTime()
   337 
   643   for (i <- (0 to n)) code
   338 def get_row(game: String, y: Int) = indexes.map(col => game(y * maxValue + col))
   644   val end = System.nanoTime()
   339 def get_col(game: String, x: Int) = indexes.map(row => game(x + row * maxValue))
   645   (end - start) / 1.0e9
   340 
   646 }
   341 def get_box(game: String, pos: Pos): List[Char] = {
   647 
   342     def base(p: Int): Int = (p / 3) * 3
   648 val list = (1L to 10_000_000L).toList
   343     val x0 = base(pos._1)
   649 time_needed(10, for (n <- list) yield n + 42)
   344     val y0 = base(pos._2)
   650 time_needed(10, for (n <- list.par) yield n + 42)
   345     for (x <- (x0 until x0 + 3).toList;
   651 
   346          y <- (y0 until y0 + 3).toList) yield game(x + y * maxValue)
   652 // ...but par does not make everything faster
   347 }         
   653 
   348 
   654 list.sum
   349 
   655 list.par.sum
   350 def update(game: String, pos: Int, value: Char): String = 
   656 
   351   game.updated(pos, value)
   657 time_needed(10, list.sum)
   352 
   658 time_needed(10, list.par.sum)
   353 def toAvoid(game: String, pos: Pos): List[Char] = 
   659 
   354   (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos))
   660 
   355 
   661 // Mutable vs Immutable
   356 def candidates(game: String, pos: Pos): List[Char] = 
   662 //======================
   357   allValues.diff(toAvoid(game, pos))
   663 //
   358 
   664 // Remember:
   359 def search(game: String): List[String] = {
   665 // - no vars, no ++i, no +=
   360   if (isDone(game)) List(game)
   666 // - no mutable data-structures (no Arrays, no ListBuffers)
   361   else 
   667 
   362     candidates(game, emptyPosition(game)).
   668 // But what the heck....lets try to count to 1 Mio in parallel
   363       map(c => search(update(game, empty(game), c))).flatten
       
   364 }
       
   365 
       
   366 
       
   367 def search1T(games: List[String]): Option[String] = games match {
       
   368   case Nil => None
       
   369   case game::rest => {
       
   370     if (isDone(game)) Some(game)
       
   371     else {
       
   372       val cs = candidates(game, emptyPosition(game))
       
   373       search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
       
   374     }
       
   375   }
       
   376 }
       
   377 
       
   378 def pretty(game: String): String = 
       
   379   "\n" + (game.sliding(maxValue, maxValue).mkString(",\n"))
       
   380 
       
   381 
       
   382 // tail recursive version that searches 
       
   383 // for all solutions
       
   384 
       
   385 def searchT(games: List[String], sols: List[String]): List[String] = games match {
       
   386   case Nil => sols
       
   387   case game::rest => {
       
   388     if (isDone(game)) searchT(rest, game::sols)
       
   389     else {
       
   390       val cs = candidates(game, emptyPosition(game))
       
   391       searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
       
   392     }
       
   393   }
       
   394 }
       
   395 
       
   396 searchT(List(game3), List()).map(pretty)
       
   397 
       
   398 
       
   399 // tail recursive version that searches 
       
   400 // for a single solution
       
   401 
       
   402 def search1T(games: List[String]): Option[String] = games match {
       
   403   case Nil => None
       
   404   case game::rest => {
       
   405     if (isDone(game)) Some(game)
       
   406     else {
       
   407       val cs = candidates(game, emptyPosition(game))
       
   408       search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
       
   409     }
       
   410   }
       
   411 }
       
   412 
       
   413 search1T(List(game3)).map(pretty)
       
   414 time_needed(10, search1T(List(game3)))
       
   415 
       
   416 
       
   417 // game with multiple solutions
       
   418 val game3 = """.8...9743
       
   419               |.5...8.1.
       
   420               |.1.......
       
   421               |8....5...
       
   422               |...8.4...
       
   423               |...3....6
       
   424               |.......7.
       
   425               |.3.5...8.
       
   426               |9724...5.""".stripMargin.replaceAll("\\n", "")
       
   427 
       
   428 searchT(List(game3), Nil).map(pretty)
       
   429 search1T(List(game3)).map(pretty)
       
   430 
       
   431 // Moral: Whenever a recursive function is resource-critical
       
   432 // (i.e. works with large recursion depth), then you need to
       
   433 // write it in tail-recursive fashion.
       
   434 // 
   669 // 
   435 // Unfortuantely, Scala because of current limitations in 
   670 // requires
   436 // the JVM is not as clever as other functional languages. It can 
   671 // scala-cli --extra-jars scala- parallel-collections_3-1.0.4.jar
   437 // only optimise "self-tail calls". This excludes the cases of 
   672 
   438 // multiple functions making tail calls to each other. Well,
   673 import scala.collection.parallel.CollectionConverters._
   439 // nothing is perfect. 
   674 
   440 
   675 def test() = {
       
   676   var cnt = 0
       
   677 
       
   678   for(i <- (1 to 100_000).par) cnt += 1
       
   679 
       
   680   println(s"Should be 100000: $cnt")
       
   681 }
       
   682 
       
   683 test()
       
   684 
       
   685 // Or
       
   686 // Q: Count how many elements are in the intersections of 
       
   687 //    two sets?
       
   688 // A; IMPROPER WAY (mutable counter)
       
   689 
       
   690 def count_intersection(A: Set[Int], B: Set[Int]) : Int = {
       
   691   var count = 0
       
   692   for (x <- A.par; if B contains x) count += 1 
       
   693   count
       
   694 }
       
   695 
       
   696 val A = (0 to 999).toSet
       
   697 val B = (0 to 999 by 4).toSet
       
   698 
       
   699 count_intersection(A, B)
       
   700 
       
   701 // but do not try to add .par to the for-loop above
       
   702 
       
   703 
       
   704 //propper parallel version
       
   705 def count_intersection2(A: Set[Int], B: Set[Int]) : Int = 
       
   706   A.par.count(x => B contains x)
       
   707 
       
   708 count_intersection2(A, B)
       
   709 
       
   710