progs/lecture2.scala
changeset 504 7653dd662db3
parent 491 2a30c7dfe3ed
equal deleted inserted replaced
503:e0ee3aa6334b 504:7653dd662db3
     5 // - Higher-Order Functions (short-hand notation)
     5 // - Higher-Order Functions (short-hand notation)
     6 // - maps (behind for-comprehensions)
     6 // - maps (behind for-comprehensions)
     7 // - Pattern-Matching
     7 // - Pattern-Matching
     8 // - Recursion
     8 // - Recursion
     9 
     9 
       
    10 
       
    11 // Function Definitions
       
    12 //======================
       
    13 
       
    14 // The general scheme for a function: you have to give a 
       
    15 // type to each argument and a return type of the function
       
    16 //
       
    17 //  def fname(arg1: ty1, arg2: ty2,..., argn: tyn): rty = {
       
    18 //     ....
       
    19 //  }
       
    20 
       
    21 def incr(x: Int) : Int = x + 1
       
    22 incr(42)
       
    23 
       
    24 def add(x: Int, y: Int) : Int = x + y
       
    25 
       
    26 
       
    27 def average(ls: List[Int]) : Option[Int] = {
       
    28   val s = ls.sum
       
    29   val l = ls.length
       
    30   if (l == 0) None else Some(s / l)
       
    31 }
       
    32 
       
    33 average(List(1,2,3,4))
       
    34 average(List())
       
    35 
       
    36 
       
    37 
       
    38 
    10 // The Option Type
    39 // The Option Type
    11 //=================
    40 //=================
    12 
    41 
    13 // in Java, if something unusually happens, you return null 
    42 // in Java, if something unusually happens, you return null 
    14 // or raise an exception
    43 // or raise an exception
    43 foo_calculation(0)
    72 foo_calculation(0)
    44 
    73 
    45 safe_div(10 + 5, 3)  
    74 safe_div(10 + 5, 3)  
    46 
    75 
    47 List(1,2,3,4,5,6).indexOf(7)
    76 List(1,2,3,4,5,6).indexOf(7)
    48 List[Int](3,4,5).min
    77 List[Int]().min
    49 List[Int]().minOption
    78 List[Int](1,2,3).minOption
    50 
    79 
    51 
    80 
    52 
    81 
    53 // better error handling with Options (no exceptions)
    82 // better error handling with Options (no exceptions)
    54 //
    83 //
    56 //
    85 //
    57 
    86 
    58 import scala.util._      // Try,...
    87 import scala.util._      // Try,...
    59 import io.Source         // fromURL
    88 import io.Source         // fromURL
    60 
    89 
    61 val my_url = "https://nms.kcl.ac.uk/christian.urban/"
    90 //val my_url = "https://nms.kcl.ac.uk/christian.urban/"
    62 
    91 val my_url = "https://urbanchr.github.io/"
    63 Source.fromURL(my_url)("ISO-8859-1").mkString
    92 
    64 Source.fromURL(my_url)("ISO-8859-1").getLines().toList
    93 println(Try(Source.fromURL(my_url)(using "ISO-8859-1").mkString).toOption)
    65 
    94 
    66 Try(Source.fromURL(my_url)("ISO-8859-1").mkString).getOrElse("")
    95 .mkString  
    67 
    96 Source.fromURL(my_url)(using "ISO-8859-1").getLines().toList
    68 Try(Some(Source.fromURL(my_url)("ISO-8859-1").mkString)).getOrElse(None)
    97 
       
    98 Try(Source.fromURL(my_url)(using "ISO-8859-1").mkString).getOrElse("")
       
    99 
       
   100 Try(Some(Source.fromURL(my_url)(using "ISO-8859-1").mkString)).getOrElse(None)
    69 
   101 
    70 
   102 
    71 // the same for files
   103 // the same for files
    72 
   104 
    73 Try(Some(Source.fromFile("test.txt")("ISO-8859-1").mkString)).getOrElse(None)
   105 Try(Some(Source.fromFile("test.txt")(using "ISO-8859-1").mkString)).getOrElse(None)
    74 
   106 
    75 Try(Source.fromFile("test.txt")("ISO-8859-1").mkString).toOption
   107 Try(Source.fromFile("test.txt")(using "ISO-8859-1").mkString).toOption
    76 
   108 
    77 Using(Source.fromFile("test.txt")("ISO-8859-1"))(_.mkString).toOption
   109 Using(Source.fromFile("test.txt")(using "ISO-8859-1"))(_.mkString).toOption
    78 
   110 
    79 // how to implement a function for reading 
   111 // how to implement a function for reading 
    80 // (lines) from files...
   112 // (lines) from files...
    81 //
   113 //
    82 def get_contents(name: String) : List[String] = 
   114 def get_contents(name: String) : List[String] = 
    83   Source.fromFile(name)("ISO-8859-1").getLines().toList
   115   Source.fromFile(name)(using "ISO-8859-1").getLines().toList
    84 
   116 
    85 get_contents("text.txt")
   117 get_contents("text.txt")
    86 get_contents("test.txt")
   118 get_contents("test.txt")
    87 
   119 
    88 // slightly better - return Nil
   120 // slightly better - return Nil
    89 def get_contents(name: String) : List[String] = 
   121 def get_contents(name: String) : List[String] = 
    90   Try(Source.fromFile(name)("ISO-8859-1").getLines.toList).getOrElse(List())
   122   Try(Source.fromFile(name)(using "ISO-8859-1").getLines.toList).getOrElse(List())
    91 
   123 
    92 get_contents("text.txt")
   124 get_contents("text.txt")
    93 
   125 
    94 // much better - you record in the type that things can go wrong 
   126 // much better - you record in the type that things can go wrong 
    95 def get_contents(name: String) : Option[List[String]] = 
   127 def get_contents(name: String) : Option[List[String]] = 
    96   Try(Some(Source.fromFile(name)("ISO-8859-1").getLines().toList)).getOrElse(None)
   128   Try(Some(Source.fromFile(name)(using "ISO-8859-1").getLines().toList)).getOrElse(None)
    97 
   129 
    98 get_contents("text.txt")
   130 get_contents("text.txt")
    99 get_contents("test.txt")
   131 get_contents("test.txt")
   100 
   132 
   101 
   133 
   173 // minOption 
   205 // minOption 
   174 // maxOption 
   206 // maxOption 
   175 // minByOption 
   207 // minByOption 
   176 // maxByOption
   208 // maxByOption
   177 
   209 
       
   210 def altproduct(xs: List[Int]) : List[Int] = xs match {
       
   211   case Nil => Nil
       
   212   case (x::y::xs) => x * y :: altproduct(y::xs)
       
   213   case (x::Nil) => List(x)
       
   214 }
       
   215 
       
   216 altproduct(List(1,2,3,4,5))
       
   217 
       
   218 def powerset(xs: Set[Int]) : Set[Set[Int]] = {
       
   219   if (xs == Set()) Set(Set())
       
   220   else {
       
   221     powerset(xs.tail) ++ powerset(xs.tail).map(_ + xs.head)
       
   222   }
       
   223 }     
       
   224 
       
   225 powerset(Set(1,2,3)).mkString("\n")
       
   226 
   178 // Higher-Order Functions
   227 // Higher-Order Functions
   179 //========================
   228 //========================
   180 
   229 
   181 // functions can take functions as arguments
   230 // functions can take functions as arguments
   182 // and produce functions as result
   231 // and produce functions as result
   183 
   232 
       
   233 val foo = for (n <- (1 to 10).toList) yield n * n
       
   234 
       
   235 
   184 def even(x: Int) : Boolean = x % 2 == 0
   236 def even(x: Int) : Boolean = x % 2 == 0
       
   237 
       
   238 even(2)
       
   239 even(3)
       
   240 
       
   241 def even(x: Int) : Boolean = x % 2 == 1
       
   242 
       
   243 val foo_fun = even
       
   244 
       
   245 foo_fun(2)
       
   246 foo_fun(3)
       
   247 
       
   248 
   185 def odd(x: Int) : Boolean = x % 2 == 1
   249 def odd(x: Int) : Boolean = x % 2 == 1
   186 
   250 
   187 def inc(x: Int) : Int = x + 1
   251 def inc(x: Int) : Int = x + 1
   188 val lst = (1 to 10).toList
   252 val lst = (1 to 10).toList
   189 
   253 
   190 lst.filter(odd)
   254 lst.filter(odd)
   191 lst.exists(even)
   255 lst.find(even)
       
   256 lst.find(_ % 2 == 0)
       
   257 lst.sortWith(_ < _)  // (x,y) => x < y
       
   258 lst.find(_ > 4)
   192 lst.count(odd)
   259 lst.count(odd)
   193 
   260 
   194 lst.filter(_ % 2 == 0)
   261 lst.filter(_ % 2 == 0)
   195 
   262 
   196 
   263 
   464 
   531 
   465 my_flatten(List(None, Some(1), Some(2), None, Some(3)))
   532 my_flatten(List(None, Some(1), Some(2), None, Some(3)))
   466 
   533 
   467 
   534 
   468 
   535 
   469  
       
   470 
       
   471 
       
   472 
       
   473 
   536 
   474 // Recursion
   537 // Recursion
   475 //===========
   538 //===========
       
   539 
       
   540 // my_length
       
   541 
       
   542 def my_length(xs: List[Int]) : Int = {
       
   543   if (xs == Nil) 0 else 1 + my_length(xs.tail)
       
   544 }
       
   545 
       
   546 def my_sum(xs: List[Int]) : Int = {
       
   547   if (xs == Nil) 0 else xs.head + my_sum(xs.tail)
       
   548 }
       
   549 
       
   550 my_sum((1 to 100).toList)
       
   551 my_length(List())
       
   552 
       
   553 /* my_map */
       
   554 for (n <- List[Int](1,2,3)) yield n * n
       
   555 
       
   556 List(1,2,3) ::: List(3,4,5)
       
   557 3 :: List(3,4,5)
       
   558 
       
   559 def my_map(xs: List[Int], f : Int => Int ) : List[Int] = {
       
   560   if (xs == Nil) Nil 
       
   561   else f(xs.head) :: my_map(xs.tail, f)
       
   562 }
       
   563 
       
   564 def square(n: Int) : Int = n * n
       
   565 
       
   566 
       
   567 my_map(List(1,2,3), square)
       
   568 
       
   569 
       
   570 
       
   571 
       
   572 /* powerset */
       
   573 
       
   574 def powerset(xs: Set[Int]) : Set[Set[Int]] = {
       
   575   if (xs == Set()) Set(Set())
       
   576   else powerset(xs.tail) ++ powerset(xs.tail).map(_ + xs.head)
       
   577 }
       
   578 
       
   579 
       
   580 
       
   581 
       
   582 
       
   583 /* on lists */
       
   584 def powerset(xs: List[Int]) : List[List[Int]] = {
       
   585   if (xs == Nil) List(Nil)
       
   586   else powerset(xs.tail) ::: powerset(xs.tail).map(xs.head :: _)
       
   587 }
       
   588 
   476 
   589 
   477 
   590 
   478 /* Say you have characters a, b, c.
   591 /* Say you have characters a, b, c.
   479    What are all the combinations of a certain length?
   592    What are all the combinations of a certain length?
   480 
   593 
   523 import io.Source
   636 import io.Source
   524 import scala.util._
   637 import scala.util._
   525 
   638 
   526 // gets the first 10K of a web-page
   639 // gets the first 10K of a web-page
   527 def get_page(url: String) : String = {
   640 def get_page(url: String) : String = {
   528   Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
   641   Try(Source.fromURL(url)(using "ISO-8859-1").take(10000).mkString).
   529     getOrElse { println(s"  Problem with: $url"); ""}
   642     getOrElse { println(s"  Problem with: $url"); ""}
   530 }
   643 }
   531 
   644 
   532 // regex for URLs and emails
   645 // regex for URLs and emails
   533 val http_pattern = """"https?://[^"]*"""".r
   646 val http_pattern = """"https?://[^"]*"""".r
   553     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
   666     for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
   554   }
   667   }
   555 }
   668 }
   556 
   669 
   557 // some starting URLs for the crawler
   670 // some starting URLs for the crawler
   558 val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
   671 //val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
       
   672 val startURL = """https://urbanchr.github.io/"""
       
   673 
   559 
   674 
   560 crawl(startURL, 2)
   675 crawl(startURL, 2)
   561 
   676 
   562 
   677 
   563 // a primitive email harvester
   678 // a primitive email harvester
   580 // scala -cp scala-parallel-collections_2.13-0.2.0.jar 
   695 // scala -cp scala-parallel-collections_2.13-0.2.0.jar 
   581 // import scala.collection.parallel.CollectionConverters._
   696 // import scala.collection.parallel.CollectionConverters._
   582 
   697 
   583 
   698 
   584 
   699 
   585 
   700 def powerset(xs: Set[Int]) : Set[Set[Int]] = {
       
   701   if (xs == Set()) Set(Set())
       
   702   else {
       
   703     val ps = powerset(xs.tail)  
       
   704     ps ++ ps.map(_ + xs.head)
       
   705   }
       
   706 }  
       
   707 
       
   708 def psubsets(xs: Set[Int]) = 
       
   709   powerset(xs) -- Set(Set(), xs) 
       
   710 
       
   711 //def psubsets(xs: Set[Int]) = 
       
   712 //  xs.subsets.toList -- Set(Set(), xs)  
       
   713 
       
   714 def splits(xs: Set[Int]) :  Set[(Set[Int], Set[Int])] =
       
   715   psubsets(xs).map(s => (s, xs -- s))
       
   716 
       
   717 
       
   718 
       
   719 enum Tree {
       
   720   case Num(i: Int)
       
   721   case Add(l: Tree, r: Tree)
       
   722   case Sub(l: Tree, r: Tree)
       
   723   case Mul(l: Tree, r: Tree)
       
   724   case Div(l: Tree, r: Tree)
       
   725 }
       
   726 import Tree._
       
   727 
       
   728 def pp(tr: Tree) : String = tr match {
       
   729   case Num(n) => s"$n"
       
   730   case Add(l, r) => s"(${pp(l)} + ${pp(r)})"
       
   731   case Sub(l, r) => s"(${pp(l)} - ${pp(r)})"
       
   732   case Mul(l, r) => s"(${pp(l)} * ${pp(r)})"
       
   733   case Div(l, r) => s"(${pp(l)} / ${pp(r)})"
       
   734 }
       
   735 
       
   736 def search(nums: Set[Int]) : Set[Tree] =  nums.size match {
       
   737   case 0 => Set()
       
   738   case 1 => Set(Num(nums.head))
       
   739   case 2 => {
       
   740     val l = nums.head
       
   741     val r = nums.tail.head
       
   742     Set(Add(Num(l), Num(r)), 
       
   743         Mul(Num(l), Num(r)))
       
   744         ++ Option.when(l <= r)(Sub(Num(r), Num(l)))
       
   745         ++ Option.when(l > r)(Sub(Num(l), Num(r)))
       
   746         ++ Option.when(r > 0 && l % r == 0)(Div(Num(l), Num(r)))
       
   747         ++ Option.when(l > 0 && r % l == 0)(Div(Num(r), Num(l)))
       
   748   }
       
   749   case xs => {
       
   750     val spls = splits(nums)
       
   751     val subtrs = 
       
   752       for ((lspls, rspls) <- spls;
       
   753            lt <- search(lspls); 
       
   754           rt <- search(rspls)) yield {
       
   755         Set(Add(lt, rt), Sub(lt, rt),
       
   756             Mul(lt, rt), Div(lt, rt))
       
   757     } 
       
   758     subtrs.flatten
       
   759   }
       
   760 }
       
   761 
       
   762 println(search(Set(1,2,3,4)).mkString("\n"))
       
   763 
       
   764 def eval(tr: Tree) : Option[Int] = tr match {
       
   765   case Num(n) => Some(n)
       
   766   case Add(l, r) => 
       
   767     for (rl <- eval(l); rr <- eval(r)) yield rl + rr
       
   768   case Mul(l, r) => 
       
   769     for (rl <- eval(l); rr <- eval(r)) yield rl * rr  
       
   770   case Sub(l, r) => 
       
   771     for (rl <- eval(l); rr <- eval(r);
       
   772          if 0 <= rl - rr) yield rl - rr   
       
   773   case Div(l, r) => 
       
   774     for (rl <- eval(l); rr <- eval(r);
       
   775          if rr > 0 && rl % rr == 0) yield rl / rr          
       
   776 }
       
   777 
       
   778 eval(Add(Num(1), Num(2)))
       
   779 eval(Mul(Add(Num(1), Num(2)), Num(4)))
       
   780 eval(Sub(Num(3), Num(2)))
       
   781 eval(Sub(Num(3), Num(6)))
       
   782 eval(Div(Num(6), Num(2)))
       
   783 eval(Div(Num(6), Num(4)))
       
   784 
       
   785 def time_needed[T](n: Int, code: => T) = {
       
   786   val start = System.nanoTime()
       
   787   for (i <- (0 to n)) code
       
   788   val end = System.nanoTime()
       
   789   (end - start) / 1.0e9
       
   790 }
       
   791 
       
   792 
       
   793 import scala.collection.parallel.CollectionConverters._
       
   794 
       
   795 def check(xs: Set[Int], target: Int) =
       
   796   search(xs).find(eval(_) == Some(target))
       
   797 
       
   798 for (sol <- check(Set(50, 5, 4, 9, 10, 8), 560)) {
       
   799   println(s"${pp(sol)} => ${eval(sol)}")
       
   800 }
       
   801 
       
   802 
       
   803 
       
   804 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560))
       
   805 
       
   806 
       
   807 println(check(Set(25, 5, 2, 10, 7, 1), 986).mkString("\n"))
       
   808 
       
   809 for (sol <- check(Set(25, 5, 2, 10, 7, 1), 986)) {
       
   810   println(s"${pp(sol)} => ${eval(sol)}")
       
   811 }
       
   812 
       
   813 for (sol <- check(Set(25, 5, 2, 10, 7, 1), -1)) {
       
   814   println(s"${pp(sol)} => ${eval(sol)}")
       
   815 }
       
   816 
       
   817 for (sol <- check(Set(100, 25, 75, 50, 7, 10), 360)) {
       
   818   println(s"${pp(sol)} => ${eval(sol)}")
       
   819 }
       
   820 time_needed(1, check(Set(100, 25, 75, 50, 7, 10), 360))
       
   821 
       
   822 
       
   823 
       
   824 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), 986))
       
   825 time_needed(1, check(Set(25, 5, 2, 10, 7, 1), -1))
       
   826 
       
   827 
       
   828 def generate(nums: Set[Int]) : Set[(Tree, Int)] =  nums.size match {
       
   829   case 0 => Set()
       
   830   case 1 => Set((Num(nums.head), nums.head))
       
   831   case xs => {
       
   832     val spls = splits(nums)
       
   833     val subtrs =
       
   834       for ((lspls, rspls) <- spls;
       
   835            (lt, ln) <- generate(lspls); 
       
   836            (rt, rn) <- generate(rspls)) yield {
       
   837         Set((Add(lt, rt), ln + rn),
       
   838             (Mul(lt, rt), ln * rn))
       
   839         ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) 
       
   840         ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn))
       
   841         ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn))
       
   842         ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln))
       
   843     } 
       
   844     subtrs.flatten
       
   845   }
       
   846 }
       
   847 
       
   848 def check2(xs: Set[Int], target: Int) =
       
   849   generate(xs).find(_._2 == target)
       
   850 
       
   851 for ((sol, ev) <- check2(Set(50, 5, 4, 9, 10, 8), 560)) {
       
   852   println(s"${pp(sol)} => ${eval(sol)} / $ev")
       
   853 }
       
   854 
       
   855 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560))
       
   856 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), 560))
       
   857 
       
   858 time_needed(1, check(Set(50, 5, 4, 9, 10, 8), -1))
       
   859 time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), -1))
   586 
   860 
   587 // Jumping Towers
   861 // Jumping Towers
   588 //================
   862 //================
   589 
   863 
   590 
   864