progs/lecture4.scala
changeset 494 253d1ccb65de
parent 481 e03a0100ec46
equal deleted inserted replaced
493:244df77507c2 494:253d1ccb65de
     1 // Scala Lecture 4
     1 // Scala Lecture 4
     2 //=================
     2 //=================
     3 
     3 
     4 // pattern-matching
     4 //===================
       
     5 // polymorphic types
       
     6 // (algebraic) datatypes and pattern-matching
       
     7 // extensions and implicits
     5 // tail-recursion
     8 // tail-recursion
     6 // polymorphic types
     9 
       
    10 
       
    11 
       
    12 // You do not want to write functions like contains, first, 
       
    13 // length and so on for every type of lists.
       
    14 
       
    15 def length_int_list(lst: List[Int]): Int = lst match {
       
    16   case Nil => 0
       
    17   case _::xs => 1 + length_int_list(xs)
       
    18 }
       
    19 
       
    20 length_int_list(List(1, 2, 3, 4))
       
    21 
       
    22 def length_string_list(lst: List[String]): Int = lst match {
       
    23   case Nil => 0
       
    24   case _::xs => 1 + length_string_list(xs)
       
    25 }
       
    26 
       
    27 length_string_list(List("1", "2", "3", "4"))
       
    28 
       
    29 
       
    30 // you can make the function parametric in type(s)
       
    31 
       
    32 def length[A](lst: List[A]): Int = lst match {
       
    33   case Nil => 0
       
    34   case x::xs => 1 + length(xs)
       
    35 }
       
    36 length(List("1", "2", "3", "4"))
       
    37 length(List(1, 2, 3, 4))
       
    38 
       
    39 
       
    40 length[String](List(1, 2, 3, 4))
       
    41 
       
    42 
       
    43 def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
       
    44   case Nil => Nil
       
    45   case x::xs => f(x)::map(xs, f) 
       
    46 }
       
    47 
       
    48 map(List(1, 2, 3, 4), (x: Int) => x.toString)
       
    49 
       
    50                                                                 
       
    51 // Type inference is local in Scala
       
    52 
       
    53 def id[T](x: T) : T = x
       
    54 
       
    55 val x = id(322)          // Int
       
    56 val y = id("hey")        // String
       
    57 val z = id(Set(1,2,3,4)) // Set[Int]
       
    58 
       
    59 
       
    60 // The type variable concept in Scala can get 
       
    61 // really complicated.
       
    62 //
       
    63 // - variance (OO)
       
    64 // - bounds (subtyping)
       
    65 // - quantification
       
    66 
       
    67 // Java has issues with this too: Java allows
       
    68 // to write the following incorrect code, and
       
    69 // only recovers by raising an exception
       
    70 // at runtime.
       
    71 
       
    72 // Object[] arr = new Integer[10];
       
    73 // arr[0] = "Hello World";
       
    74 
       
    75 
       
    76 // Scala gives you a compile-time error, which
       
    77 // is much better.
       
    78 
       
    79 var arr = Array[Int]()
       
    80 arr(0) = "Hello World"
       
    81 
     7 
    82 
     8 
    83 
     9 
    84 
    10 // Pattern Matching
    85 // Pattern Matching
    11 //==================
    86 //==================
    38 
   113 
    39 len(Nil)
   114 len(Nil)
    40 len(List(1,2,3,4))
   115 len(List(1,2,3,4))
    41 
   116 
    42 
   117 
    43 List(1,2,3,4).map(x => x * x)
   118 //=================
    44 
   119 // Trees (example of an Algebraic Datatype)
    45 def my_map_int(lst: List[Int], f: Int => Int) : List[Int] = 
       
    46   lst match {
       
    47     case Nil => Nil
       
    48     case foo::xs => f(foo) :: my_map_int(xs, f)
       
    49   }
       
    50 
       
    51 def my_map_option(opt: Option[Int], f: Int => Int) : Option[Int] = 
       
    52   opt match {
       
    53     case None => None
       
    54     case Some(x) => {
       
    55       Some(f(x))
       
    56     }
       
    57   }
       
    58 
       
    59 my_map_option(None, x => x * x)
       
    60 my_map_option(Some(8), x => x * x)
       
    61 
       
    62 
       
    63 // you can also have cases combined
       
    64 def season(month: String) : String = month match {
       
    65   case "March" | "April" | "May" => "It's spring"
       
    66   case "June" | "July" | "August" => "It's summer"
       
    67   case "September" | "October" | "November" => "It's autumn"
       
    68   case "December" => "It's winter"
       
    69   case "January" | "February" => "It's unfortunately winter"
       
    70   case _ => "Wrong month"
       
    71 }
       
    72 
       
    73 // pattern-match on integers
       
    74 
       
    75 def fib(n: Int) : Int = n match { 
       
    76   case 0 | 1 => 1
       
    77   case _ => fib(n - 1) + fib(n - 2)
       
    78 }
       
    79 
       
    80 fib(10)
       
    81 
       
    82 // pattern-match on results
       
    83 
       
    84 // Silly: fizz buzz
       
    85 def fizz_buzz(n: Int) : String = (n % 3, n % 5) match {
       
    86   case (0, 0) => "fizz buzz"
       
    87   case (0, _) => "fizz"
       
    88   case (_, 0) => "buzz"
       
    89   case _ => n.toString  
       
    90 }
       
    91 
       
    92 for (n <- 1 to 20) 
       
    93  println(fizz_buzz(n))
       
    94 
       
    95 // guards in pattern-matching
       
    96 
       
    97 def foo(xs: List[Int]) : String = xs match {
       
    98   case Nil => s"this list is empty"
       
    99   case x :: xs if x % 2 == 0 
       
   100      => s"the first elemnt is even"
       
   101   case x if len(x) ==
       
   102      => s"this list has exactly two elements"   
       
   103   case x :: y :: rest if x == y
       
   104      => s"this has two elemnts that are the same"
       
   105   case hd :: tl => s"this list is standard $hd::$tl"
       
   106 }
       
   107 
       
   108 foo(Nil)
       
   109 foo(List(1,2,3))
       
   110 foo(List(1,1))
       
   111 foo(List(1,1,2,3))
       
   112 foo(List(2,2,2,3))
       
   113 
       
   114 
       
   115 
       
   116 
       
   117 abstract class Colour
       
   118 case object Red extends Colour 
       
   119 case object Green extends Colour 
       
   120 case object Blue extends Colour
       
   121 case object Yellow extends Colour
       
   122 
       
   123 
       
   124 def fav_colour(c: Colour) : Boolean = c match {
       
   125   case Green => true
       
   126   case Red => true
       
   127   case _  => false 
       
   128 }
       
   129 
       
   130 fav_colour(Blue)
       
   131 
       
   132 
       
   133 // ... a tiny bit more useful: Roman Numerals
       
   134 
       
   135 sealed abstract class RomanDigit 
       
   136 case object I extends RomanDigit 
       
   137 case object V extends RomanDigit 
       
   138 case object X extends RomanDigit 
       
   139 case object L extends RomanDigit 
       
   140 case object C extends RomanDigit 
       
   141 case object D extends RomanDigit 
       
   142 case object M extends RomanDigit 
       
   143 
       
   144 type RomanNumeral = List[RomanDigit] 
       
   145 
       
   146 List(I, M,C,D,X,X,V,I,I, A)
       
   147 
       
   148 /*
       
   149 I    -> 1
       
   150 II   -> 2
       
   151 III  -> 3
       
   152 IV   -> 4
       
   153 V    -> 5
       
   154 VI   -> 6
       
   155 VII  -> 7
       
   156 VIII -> 8
       
   157 IX   -> 9
       
   158 X    -> 10
       
   159 */
       
   160 
       
   161 def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
       
   162   case Nil => 0
       
   163   case M::r    => 1000 + RomanNumeral2Int(r)  
       
   164   case C::M::r => 900 + RomanNumeral2Int(r)
       
   165   case D::r    => 500 + RomanNumeral2Int(r)
       
   166   case C::D::r => 400 + RomanNumeral2Int(r)
       
   167   case C::r    => 100 + RomanNumeral2Int(r)
       
   168   case X::C::r => 90 + RomanNumeral2Int(r)
       
   169   case L::r    => 50 + RomanNumeral2Int(r)
       
   170   case X::L::r => 40 + RomanNumeral2Int(r)
       
   171   case X::r    => 10 + RomanNumeral2Int(r)
       
   172   case I::X::r => 9 + RomanNumeral2Int(r)
       
   173   case V::r    => 5 + RomanNumeral2Int(r)
       
   174   case I::V::r => 4 + RomanNumeral2Int(r)
       
   175   case I::r    => 1 + RomanNumeral2Int(r)
       
   176 }
       
   177 
       
   178 RomanNumeral2Int(List(I,V))             // 4
       
   179 RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid Roman number)
       
   180 RomanNumeral2Int(List(V,I))             // 6
       
   181 RomanNumeral2Int(List(I,X))             // 9
       
   182 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
       
   183 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
       
   184 
   120 
   185 abstract class Tree
   121 abstract class Tree
   186 case class Leaf(x: Int)
   122 case class Leaf(x: Int) extends Tree
   187 case class Branch(tl: Tree, tr: Tree)
   123 case class Node(s: String, left: Tree, right: Tree) extends Tree 
       
   124 
       
   125 val lf = Leaf(20)
       
   126 val tr = Node("foo", Leaf(10), Leaf(23))
       
   127 
       
   128 val lst : List[Tree] = List(lf, tr)
       
   129 
       
   130 
   188 
   131 
   189 
   132 
   190 abstract class Rexp
   133 abstract class Rexp
   191 case object ZERO extends Rexp                      // matches nothing
   134 case object ZERO extends Rexp                      // matches nothing
   192 case object ONE extends Rexp                       // matches the empty string
   135 case object ONE extends Rexp                       // matches the empty string
   203   case SEQ(r1, r2) => 1 + List(depth(r1), depth(r2)).max
   146   case SEQ(r1, r2) => 1 + List(depth(r1), depth(r2)).max
   204   case STAR(r1) => 1 + depth(r1)
   147   case STAR(r1) => 1 + depth(r1)
   205 }
   148 }
   206 
   149 
   207 
   150 
   208 // Trees (example of an Algebraic Datatype)
   151 
   209 
       
   210 
       
   211 abstract class Tree
       
   212 case class Leaf(x: Int) extends Tree
       
   213 case class Node(s: String, left: Tree, right: Tree) extends Tree 
       
   214 
       
   215 val lf = Leaf(20)
       
   216 val tr = Node("foo", Leaf(10), Leaf(23))
       
   217 
       
   218 val lst : List[Tree] = List(lf, tr)
       
   219 
   152 
   220 
   153 
   221 
   154 
   222 // expressions (essentially trees)
   155 // expressions (essentially trees)
   223 
   156 
   325 
   258 
   326 def factT(n: BigInt, acc: BigInt): BigInt =
   259 def factT(n: BigInt, acc: BigInt): BigInt =
   327   if (n == 0) acc else factT(n - 1, n * acc)
   260   if (n == 0) acc else factT(n - 1, n * acc)
   328 
   261 
   329 
   262 
   330 factT(10, 1)
   263 factT(1000,1)
   331 println(factT(100000, 1))
   264 println(factT(100000, 1))
   332 
   265 
   333 
   266 
   334 // there is a flag for ensuring a function is tail recursive
   267 // there is a flag for ensuring a function is tail recursive
   335 import scala.annotation.tailrec
   268 import scala.annotation.tailrec
   355 // only optimise "self-tail calls". This excludes the cases of 
   288 // only optimise "self-tail calls". This excludes the cases of 
   356 // multiple functions making tail calls to each other. Well,
   289 // multiple functions making tail calls to each other. Well,
   357 // nothing is perfect. 
   290 // nothing is perfect. 
   358 
   291 
   359 
   292 
   360 
   293 // default arguments
   361 // Polymorphic Types
   294 
   362 //===================
   295 def factT(n: BigInt, acc: BigInt = 1): BigInt =
   363 
   296   if (n == 0) acc else factT(n - 1, n * acc)
   364 // You do not want to write functions like contains, first, 
   297 
   365 // length and so on for every type of lists.
   298 factT(1_000_000)
   366 
   299 
   367 def length_int_list(lst: List[Int]): Int = lst match {
   300 
       
   301 def length[A](xs: List[A]) : Int = xs match {
   368   case Nil => 0
   302   case Nil => 0
   369   case _::xs => 1 + length_int_list(xs)
   303   case _ :: tail => 1 + length(tail)
   370 }
   304 }
   371 
   305 
   372 length_int_list(List(1, 2, 3, 4))
   306 length(List.fill(100000)(1))
   373 
   307 
   374 def length_string_list(lst: List[String]): Int = lst match {
   308 def lengthT[A](xs: List[A], acc : Int = 0) : Int = xs match {
   375   case Nil => 0
   309   case Nil => acc
   376   case _::xs => 1 + length_string_list(xs)
   310   case _ :: tail => lengthT(tail, 1 + acc)
   377 }
   311 }
   378 
   312 
   379 length_string_list(List("1", "2", "3", "4"))
   313 lengthT(List.fill(100000)(1))
   380 
   314 
   381 
   315 
   382 // you can make the function parametric in type(s)
   316 
   383 
   317 
   384 def length[A](lst: List[A]): Int = lst match {
       
   385   case Nil => 0
       
   386   case x::xs => 1 + length(xs)
       
   387 }
       
   388 length(List("1", "2", "3", "4"))
       
   389 length(List(1, 2, 3, 4))
       
   390 
       
   391 
       
   392 length[String](List(1, 2, 3, 4))
       
   393 
       
   394 
       
   395 def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
       
   396   case Nil => Nil
       
   397   case x::xs => f(x)::map(xs, f) 
       
   398 }
       
   399 
       
   400 map(List(1, 2, 3, 4), (x: Int) => x.toString)
       
   401 
       
   402 
       
   403 // should be
       
   404 def first[A, B](xs: List[A], f: A => Option[B]) : Option[B] = ???
       
   405 
       
   406 // Type inference is local in Scala
       
   407 
       
   408 def id[T](x: T) : T = x
       
   409 
       
   410 val x = id(322)          // Int
       
   411 val y = id("hey")        // String
       
   412 val z = id(Set(1,2,3,4)) // Set[Int]
       
   413 
       
   414 
       
   415 // The type variable concept in Scala can get really complicated.
       
   416 //
       
   417 // - variance (OO)
       
   418 // - bounds (subtyping)
       
   419 // - quantification
       
   420 
       
   421 // Java has issues with this too: Java allows
       
   422 // to write the following incorrect code, and
       
   423 // only recovers by raising an exception
       
   424 // at runtime.
       
   425 
       
   426 // Object[] arr = new Integer[10];
       
   427 // arr[0] = "Hello World";
       
   428 
       
   429 
       
   430 // Scala gives you a compile-time error, which
       
   431 // is much better.
       
   432 
       
   433 var arr = Array[Int]()
       
   434 arr(0) = "Hello World"
       
   435 
   318 
   436 
   319 
   437 
   320 
   438 
   321 
   439 // Function definitions again
   322 // Function definitions again
   464     def i(args: Any*): String = s"${sc.s(args:_*)}\n"
   347     def i(args: Any*): String = s"${sc.s(args:_*)}\n"
   465 }
   348 }
   466 
   349 
   467 i"add ${3+2} ${3 * 3}" 
   350 i"add ${3+2} ${3 * 3}" 
   468 
   351 
   469 
       
   470 // default arguments
       
   471 
       
   472 def length[A](xs: List[A]) : Int = xs match {
       
   473   case Nil => 0
       
   474   case _ :: tail => 1 + length(tail)
       
   475 }
       
   476 
       
   477 def lengthT[A](xs: List[A], acc : Int = 0) : Int = xs match {
       
   478   case Nil => acc
       
   479   case _ :: tail => lengthT(tail, 1 + acc)
       
   480 }
       
   481 
       
   482 lengthT(List.fill(100000)(1))
       
   483 
       
   484 
       
   485 def fact(n: BigInt, acc: BigInt = 1): BigInt =
       
   486   if (n == 0) acc else fact(n - 1, n * acc)
       
   487 
       
   488 fact(10)
       
   489 
   352 
   490 
   353 
   491 
   354 
   492 // currying    (Haskell Curry)
   355 // currying    (Haskell Curry)
   493 
   356 
   670     case Nil => sols
   533     case Nil => sols
   671     case game::rest => {
   534     case game::rest => {
   672       if (isDone(game)) searchT(rest, game::sols)
   535       if (isDone(game)) searchT(rest, game::sols)
   673       else {
   536       else {
   674         val cs = candidates(game, emptyPosition(game))
   537         val cs = candidates(game, emptyPosition(game))
   675         searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
   538         searchT(cs.map(c => update(game, empty(game), c)) 
       
   539                   ::: rest, sols)
   676      }
   540      }
   677    }
   541    }
   678  }
   542  }
   679 
   543 
   680 searchT(List(game3), List()).map(pretty)
   544 searchT(List(game3), List()).map(pretty)
   728 // Imagine you want to increment strings, like
   592 // Imagine you want to increment strings, like
   729 //
   593 //
   730 //     "HAL".increment
   594 //     "HAL".increment
   731 //
   595 //
   732 // you can avoid ugly fudges, like a MyString, by
   596 // you can avoid ugly fudges, like a MyString, by
   733 // using implicit conversions.
   597 // using an extension.
   734 
   598 
   735 print("\n")
   599 
   736 print("""\n""")
   600 extension (s: String) {
   737 
       
   738 implicit class MyString(s: String) {
       
   739   def increment = s.map(c => (c + 1).toChar) 
   601   def increment = s.map(c => (c + 1).toChar) 
   740 }
   602 }
   741 
   603 
   742 "HAL".increment
   604 "HAL".increment
   743 
   605 
   744 
   606 
   745 // Abstract idea:
       
   746 // In that version implicit conversions were used to solve the 
       
   747 // late extension problem; namely, given a class C and a class T, 
       
   748 // how to have C extend T without touching or recompiling C. 
       
   749 // Conversions add a wrapper when a member of T is requested 
       
   750 // from an instance of C.
       
   751 
   607 
   752 
   608 
   753 
   609 
   754 import scala.concurrent.duration.{TimeUnit,SECONDS,MINUTES}
   610 import scala.concurrent.duration.{TimeUnit,SECONDS,MINUTES}
   755 
   611 
   756 case class Duration(time: Long, unit: TimeUnit) {
   612 case class Duration(time: Long, unit: TimeUnit) {
   757   def +(o: Duration) = 
   613   def +(o: Duration) = 
   758     Duration(time + unit.convert(o.time, o.unit), unit)
   614     Duration(time + unit.convert(o.time, o.unit), unit)
   759 }
   615 }
   760 
   616 
   761 implicit class Int2Duration(that: Int) {
   617 extension (that: Int) {
   762   def seconds = Duration(that, SECONDS)
   618   def seconds = Duration(that, SECONDS)
   763   def minutes = Duration(that, MINUTES)
   619   def minutes = Duration(that, MINUTES)
   764 }
   620 }
   765 
   621 
   766 5.seconds + 2.minutes   //Duration(125L, SECONDS )
   622 5.seconds + 2.minutes   //Duration(125L, SECONDS )
   787 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
   643 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
   788 
   644 
   789 
   645 
   790 // some convenience for typing in regular expressions
   646 // some convenience for typing in regular expressions
   791 import scala.language.implicitConversions    
   647 import scala.language.implicitConversions    
   792 import scala.language.reflectiveCalls 
       
   793 
   648 
   794 def charlist2rexp(s: List[Char]): Rexp = s match {
   649 def charlist2rexp(s: List[Char]): Rexp = s match {
   795   case Nil => ONE
   650   case Nil => ONE
   796   case c::Nil => CHAR(c)
   651   case c::Nil => CHAR(c)
   797   case c::s => SEQ(CHAR(c), charlist2rexp(s))
   652   case c::s => SEQ(CHAR(c), charlist2rexp(s))
   798 }
   653 }
   799 
   654 
   800 implicit def string2rexp(s: String): Rexp = 
   655 given Conversion[String, Rexp] = 
   801   charlist2rexp(s.toList)
   656    (s => charlist2rexp(s.toList))
   802 
   657 
   803 val r1 = STAR("ab")
   658 val r1 = STAR("ab")
   804 val r2 = STAR("hello") | STAR("world")
   659 val r2 = STAR("hello") | STAR("world")
   805 
   660 
   806 
   661 
   807 implicit def RexpOps (r: Rexp) = new {
   662 extension (r: Rexp) {
   808   def | (s: Rexp) = ALT(r, s)
   663   def | (s: Rexp) = ALT(r, s)
   809   def % = STAR(r)
   664   def % = STAR(r)
   810   def ~ (s: Rexp) = SEQ(r, s)
   665   def ~ (s: Rexp) = SEQ(r, s)
   811 }
   666 }
   812 
   667 
   813 implicit def stringOps (s: String) = new {
       
   814   def | (r: Rexp) = ALT(s, r)
       
   815   def | (r: String) = ALT(s, r)
       
   816   def % = STAR(s)
       
   817   def ~ (r: Rexp) = SEQ(s, r)
       
   818   def ~ (r: String) = SEQ(s, r)
       
   819 }
       
   820 
       
   821 //example regular expressions
   668 //example regular expressions
       
   669 
       
   670 val rex = "ab".%
   822 
   671 
   823 
   672 
   824 val digit = ("0" | "1" | "2" | "3" | "4" | 
   673 val digit = ("0" | "1" | "2" | "3" | "4" | 
   825               "5" | "6" | "7" | "8" | "9")
   674               "5" | "6" | "7" | "8" | "9")
   826 val sign = "+" | "-" | ""
   675 val sign = "+" | "-" | ""
   848 
   697 
   849 // ...to allow the notation n + m * i
   698 // ...to allow the notation n + m * i
   850 import scala.language.implicitConversions   
   699 import scala.language.implicitConversions   
   851 
   700 
   852 val i = Complex(0, 1)
   701 val i = Complex(0, 1)
   853 implicit def double2complex(re: Double) = Complex(re, 0)
   702 given Conversion[Double, Complex] = (re: Double) => Complex(re, 0)
   854 
   703 
   855 val inum1 = -2.0 + -1.5 * i
   704 val inum1 = -2.0 + -1.5 * i
   856 val inum2 =  1.0 +  1.5 * i
   705 val inum2 =  1.0 +  1.5 * i