|      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 | 
|    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 | 
|    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 = "+" | "-" | "" |