progs/lecture3.scala
changeset 158 94b11ac19b41
parent 155 371acb50643d
child 170 37b1bfcdba79
equal deleted inserted replaced
157:15f1fca879c5 158:94b11ac19b41
     4 // Pattern Matching
     4 // Pattern Matching
     5 //==================
     5 //==================
     6 
     6 
     7 // A powerful tool which is supposed to come to Java in a few years
     7 // A powerful tool which is supposed to come to Java in a few years
     8 // time (https://www.youtube.com/watch?v=oGll155-vuQ)...Scala already
     8 // time (https://www.youtube.com/watch?v=oGll155-vuQ)...Scala already
     9 // has it for many years. Other functional languages have it for
     9 // has it for many years. Other functional languages have it already for
    10 // decades. I think I would refuse to program in a language that
    10 // decades. I think I would be really upset if a programming language 
    11 // does not have pattern matching....its is just so elegant. ;o)
    11 // I have to use does not have pattern matching....its is just so 
       
    12 // useful. ;o)
    12 
    13 
    13 // The general schema:
    14 // The general schema:
    14 //
    15 //
    15 //    expression match {
    16 //    expression match {
    16 //       case pattern1 => expression1
    17 //       case pattern1 => expression1
    23 // remember
    24 // remember
    24 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
    25 val lst = List(None, Some(1), Some(2), None, Some(3)).flatten
    25 
    26 
    26 
    27 
    27 def my_flatten(xs: List[Option[Int]]): List[Int] = {
    28 def my_flatten(xs: List[Option[Int]]): List[Int] = {
    28   ...?
    29   if (xs == Nil) Nil
    29 }
    30   else if (xs.head == None) my_flatten(xs.tail)
    30 
    31   else xs.head.get :: my_flatten(xs.tail)
    31 
    32 }
    32 
    33 
    33 
    34 
       
    35 
       
    36 val lst = List(None, Some(1), Some(2), None, Some(3))
    34 
    37 
    35 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
    38 def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
    36   case Nil => Nil
    39   case Nil => Nil
    37   case None::xs => my_flatten(xs)
    40   case None::xs => my_flatten(xs)
    38   case Some(n)::xs => n::my_flatten(xs)
    41   case Some(n)::xs => n::my_flatten(xs)
    39 }
    42 }
       
    43 
       
    44 my_flatten(lst)
       
    45 
       
    46 Nil == List()
    40 
    47 
    41 
    48 
    42 // another example including a catch-all pattern
    49 // another example including a catch-all pattern
    43 def get_me_a_string(n: Int): String = n match {
    50 def get_me_a_string(n: Int): String = n match {
    44   case 0 => "zero"
    51   case 0 => "zero"
    45   case 1 => "one"
    52   case 1 => "one"
    46   case 2 => "two"
    53   case 2 => "two"
    47   case _ => "many"
    54   case _ => "many"
    48 }
    55 }
    49 
    56 
    50 get_me_a_string(0)
    57 get_me_a_string(10)
    51 
    58 
    52 // you can also have cases combined
    59 // you can also have cases combined
    53 def season(month: String) = month match {
    60 def season(month: String) = month match {
    54   case "March" | "April" | "May" => "It's spring"
    61   case "March" | "April" | "May" => "It's spring"
    55   case "June" | "July" | "August" => "It's summer"
    62   case "June" | "July" | "August" => "It's summer"
    62 // What happens if no case matches?
    69 // What happens if no case matches?
    63 
    70 
    64 println(season("foobar"))
    71 println(season("foobar"))
    65 
    72 
    66 
    73 
    67 // Collatz function on binary strings
    74 // we can also match more complicated pattern
       
    75 //
       
    76 // let's look at the Collatz function on binary strings
    68 
    77 
    69 // adding two binary strings in a very, very lazy manner
    78 // adding two binary strings in a very, very lazy manner
    70 
    79 
    71 def badd(s1: String, s2: String) : String = 
    80 def badd(s1: String, s2: String) : String = 
    72   (BigInt(s1, 2) + BigInt(s2, 2)).toString(2)
    81   (BigInt(s1, 2) + BigInt(s2, 2)).toString(2)
    73 
    82 
    74 
    83 
    75 // collatz function on binary numbers
    84 "111".dropRight(1)
       
    85 "111".last
    76 
    86 
    77 def bcollatz(s: String) : Long = (s.dropRight(1), s.last) match {
    87 def bcollatz(s: String) : Long = (s.dropRight(1), s.last) match {
    78   case ("", '1') => 1                                  // we reached 1
    88   case ("", '1') => 1                               // we reached 1
    79   case (rest, '0') => 1 + bcollatz(rest)               // even number => divide by two
    89   case (rest, '0') => 1 + bcollatz(rest)            
    80   case (rest, '1') => 1 + bcollatz(badd(s + '1', s))   // odd number => s + '1' is 2 * s + 1
    90                                   // even number => divide by two
    81                                                        //               add another s gives 3 * s + 1  
    91   case (rest, '1') => 1 + bcollatz(badd(s + '1', s))
       
    92                                   // odd number => s + '1' is 2 * s + 1
       
    93                                   // add another s gives 3 * s + 1  
    82 } 
    94 } 
    83 
    95 
    84 bcollatz(9.toBinaryString)
    96 bcollatz(6.toBinaryString)
    85 bcollatz(837799.toBinaryString)
    97 bcollatz(837799.toBinaryString)
    86 bcollatz(100000000000000000L.toBinaryString)
    98 bcollatz(100000000000000000L.toBinaryString)
    87 bcollatz(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))
    99 bcollatz(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))
    88 
   100 
    89 
   101 
    91 
   103 
    92 // User-defined Datatypes
   104 // User-defined Datatypes
    93 //========================
   105 //========================
    94 
   106 
    95 abstract class Colour
   107 abstract class Colour
    96 case class Red() extends Colour 
   108 case object Red extends Colour 
    97 case class Green() extends Colour 
   109 case object Green extends Colour 
    98 case class Blue() extends Colour
   110 case object Blue extends Colour
    99 
   111 
   100 def fav_colour(c: Colour) : Boolean = c match {
   112 def fav_colour(c: Colour) : Boolean = c match {
   101   case Red()   => false
   113   case Red   => false
   102   case Green() => true
   114   case Green => true
   103   case Blue()  => false 
   115   case Blue  => false 
   104 }
   116 }
       
   117 
       
   118 fav_colour(Green)
   105 
   119 
   106 
   120 
   107 // actually colors can be written with "object",
   121 // actually colors can be written with "object",
   108 // because they do not take any arguments
   122 // because they do not take any arguments
   109 
   123 
   110 
   124 
   111 
   125 // ... a bit more useful: Roman Numerals
   112 // Roman Numerals
   126 
   113 abstract class RomanDigit 
   127 abstract class RomanDigit 
   114 case object I extends RomanDigit 
   128 case object I extends RomanDigit 
   115 case object V extends RomanDigit 
   129 case object V extends RomanDigit 
   116 case object X extends RomanDigit 
   130 case object X extends RomanDigit 
   117 case object L extends RomanDigit 
   131 case object L extends RomanDigit 
   136   case V::r    => 5 + RomanNumeral2Int(r)
   150   case V::r    => 5 + RomanNumeral2Int(r)
   137   case I::V::r => 4 + RomanNumeral2Int(r)
   151   case I::V::r => 4 + RomanNumeral2Int(r)
   138   case I::r    => 1 + RomanNumeral2Int(r)
   152   case I::r    => 1 + RomanNumeral2Int(r)
   139 }
   153 }
   140 
   154 
   141 RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid roman number)
       
   142 RomanNumeral2Int(List(I,V))             // 4
   155 RomanNumeral2Int(List(I,V))             // 4
       
   156 RomanNumeral2Int(List(I,I,I,I))         // 4 (invalid Roman number)
   143 RomanNumeral2Int(List(V,I))             // 6
   157 RomanNumeral2Int(List(V,I))             // 6
   144 RomanNumeral2Int(List(I,X))             // 9
   158 RomanNumeral2Int(List(I,X))             // 9
   145 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   159 RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979
   146 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   160 RomanNumeral2Int(List(M,M,X,V,I,I))     // 2017
   147 
   161 
   148 
   162 
   149 
   163 
   150 // another example
   164 // another example
   151 //=================
   165 //=================
   152 
   166 
   153 // Once upon a time, in a complete fictional country there were persons...
   167 // Once upon a time, in a complete fictional country there were Persons...
   154 
   168 
   155 abstract class Person
   169 abstract class Person
   156 case class King() extends Person
   170 case object King extends Person
   157 case class Peer(deg: String, terr: String, succ: Int) extends Person
   171 case class Peer(deg: String, terr: String, succ: Int) extends Person
   158 case class Knight(name: String) extends Person
   172 case class Knight(name: String) extends Person
   159 case class Peasant(name: String) extends Person
   173 case class Peasant(name: String) extends Person
   160 
   174 case object Clown extends Person
   161 
   175 
   162 def title(p: Person): String = p match {
   176 def title(p: Person): String = p match {
   163   case King() => "His Majesty the King"
   177   case King => "His Majesty the King"
   164   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
   178   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
   165   case Knight(name) => s"Sir ${name}"
   179   case Knight(name) => s"Sir ${name}"
   166   case Peasant(name) => name
   180   case Peasant(name) => name
   167 }
   181   case Clown => "My name is Boris Johnson"
       
   182 
       
   183 }
       
   184 
       
   185 title(Clown)
       
   186 
   168 
   187 
   169 
   188 
   170 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
   189 def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
   171   case (King(), _) => true
   190   case (King, _) => true
   172   case (Peer(_,_,_), Knight(_)) => true
   191   case (Peer(_,_,_), Knight(_)) => true
   173   case (Peer(_,_,_), Peasant(_)) => true
   192   case (Peer(_,_,_), Peasant(_)) => true
   174   case (Peer(_,_,_), Clown()) => true
   193   case (Peer(_,_,_), Clown) => true
   175   case (Knight(_), Peasant(_)) => true
   194   case (Knight(_), Peasant(_)) => true
   176   case (Knight(_), Clown()) => true
   195   case (Knight(_), Clown) => true
   177   case (Clown(), Peasant(_)) => true
   196   case (Clown, Peasant(_)) => true
   178   case _ => false
   197   case _ => false
   179 }
   198 }
   180 
   199 
   181 val people = List(Knight("David"), 
   200 val people = List(Knight("David"), 
   182                   Peer("Duke", "Norfolk", 84), 
   201                   Peer("Duke", "Norfolk", 84), 
   183                   Peasant("Christian"), 
   202                   Peasant("Christian"), 
   184                   King(), 
   203                   King, 
   185                   Clown())
   204                   Clown)
   186 
   205 
   187 println(people.sortWith(superior(_, _)).mkString(", "))
   206 println(people.sortWith(superior(_, _)).mkString(", "))
   188 
   207 
   189 
   208 
   190 
   209 
   200 fact(10000)           // produces a stackoverflow
   219 fact(10000)           // produces a stackoverflow
   201 
   220 
   202 def factT(n: BigInt, acc: BigInt): BigInt =
   221 def factT(n: BigInt, acc: BigInt): BigInt =
   203   if (n == 0) acc else factT(n - 1, n * acc)
   222   if (n == 0) acc else factT(n - 1, n * acc)
   204 
   223 
       
   224 factT(10, 1)
   205 factT(100000, 1)
   225 factT(100000, 1)
   206 
   226 
   207 // there is a flag for ensuring a function is tail recursive
   227 // there is a flag for ensuring a function is tail recursive
   208 import scala.annotation.tailrec
   228 import scala.annotation.tailrec
   209 
   229 
   273 //candidates(game0, (0,0))
   293 //candidates(game0, (0,0))
   274 
   294 
   275 def pretty(game: String): String = 
   295 def pretty(game: String): String = 
   276   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   296   "\n" + (game sliding (MaxValue, MaxValue) mkString "\n")
   277 
   297 
       
   298 /////////////////////
   278 // not tail recursive 
   299 // not tail recursive 
   279 def search(game: String): List[String] = {
   300 def search(game: String): List[String] = {
   280   if (isDone(game)) List(game)
   301   if (isDone(game)) List(game)
   281   else {
   302   else {
   282     val cs = candidates(game, emptyPosition(game))
   303     val cs = candidates(game, emptyPosition(game))
   283     cs.map(c => search(update(game, empty(game), c))).toList.flatten
   304     cs.map(c => search(update(game, empty(game), c))).toList.flatten
   284   }
   305   }
   285 }
   306 }
   286 
   307 
   287 // tail recursive version that searches 
   308 // tail recursive version that searches 
   288 // for all solution
   309 // for all solutions
       
   310 
   289 def searchT(games: List[String], sols: List[String]): List[String] = games match {
   311 def searchT(games: List[String], sols: List[String]): List[String] = games match {
   290   case Nil => sols
   312   case Nil => sols
   291   case game::rest => {
   313   case game::rest => {
   292     if (isDone(game)) searchT(rest, game::sols)
   314     if (isDone(game)) searchT(rest, game::sols)
   293     else {
   315     else {
   295       searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
   317       searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols)
   296     }
   318     }
   297   }
   319   }
   298 }
   320 }
   299 
   321 
       
   322 searchT(List(game3), List()).map(pretty)
       
   323 
       
   324 
   300 // tail recursive version that searches 
   325 // tail recursive version that searches 
   301 // for a single solution
   326 // for a single solution
       
   327 
   302 def search1T(games: List[String]): Option[String] = games match {
   328 def search1T(games: List[String]): Option[String] = games match {
   303   case Nil => None
   329   case Nil => None
   304   case game::rest => {
   330   case game::rest => {
   305     if (isDone(game)) Some(game)
   331     if (isDone(game)) Some(game)
   306     else {
   332     else {
   307       val cs = candidates(game, emptyPosition(game))
   333       val cs = candidates(game, emptyPosition(game))
   308       search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
   334       search1T(cs.map(c => update(game, empty(game), c)) ::: rest)
   309     }
   335     }
   310   }
   336   }
   311 }
   337 }
       
   338 
       
   339 search1T(List(game3)).map(pretty)
   312 
   340 
   313 // game with multiple solutions
   341 // game with multiple solutions
   314 val game3 = """.8...9743
   342 val game3 = """.8...9743
   315               |.5...8.1.
   343               |.5...8.1.
   316               |.1.......
   344               |.1.......
   319               |...3....6
   347               |...3....6
   320               |.......7.
   348               |.......7.
   321               |.3.5...8.
   349               |.3.5...8.
   322               |9724...5.""".stripMargin.replaceAll("\\n", "")
   350               |9724...5.""".stripMargin.replaceAll("\\n", "")
   323 
   351 
   324 searchT(List(game3), List()).map(pretty)
   352 searchT(List(game3), Nil).map(pretty)
   325 search1T(List(game3)).map(pretty)
   353 search1T(List(game3)).map(pretty)
   326 
   354 
   327 // Moral: Whenever a recursive function is resource-critical
   355 // Moral: Whenever a recursive function is resource-critical
   328 // (i.e. works with large recursion depths), then you need to
   356 // (i.e. works with large recursion depth), then you need to
   329 // write it in tail-recursive fashion.
   357 // write it in tail-recursive fashion.
   330 // 
   358 // 
   331 // Unfortuantely, Scala because of current limitations in 
   359 // Unfortuantely, Scala because of current limitations in 
   332 // the JVM is not as clever as other functional languages. It can 
   360 // the JVM is not as clever as other functional languages. It can 
   333 // only optimise "self-tail calls". This excludes the cases of 
   361 // only optimise "self-tail calls". This excludes the cases of 
   347 def length_string_list(lst: List[String]): Int = lst match {
   375 def length_string_list(lst: List[String]): Int = lst match {
   348   case Nil => 0
   376   case Nil => 0
   349   case x::xs => 1 + length_string_list(xs)
   377   case x::xs => 1 + length_string_list(xs)
   350 }
   378 }
   351 
   379 
       
   380 def length_int_list(lst: List[Int]): Int = lst match {
       
   381   case Nil => 0
       
   382   case x::xs => 1 + length_int_list(xs)
       
   383 }
       
   384 
   352 length_string_list(List("1", "2", "3", "4"))
   385 length_string_list(List("1", "2", "3", "4"))
   353 
   386 length_int_list(List(1, 2, 3, 4))
   354 
   387 
       
   388 //-----
   355 def length[A](lst: List[A]): Int = lst match {
   389 def length[A](lst: List[A]): Int = lst match {
   356   case Nil => 0
   390   case Nil => 0
   357   case x::xs => 1 + length(xs)
   391   case x::xs => 1 + length(xs)
   358 }
   392 }
   359 
   393 length(List("1", "2", "3", "4"))
   360 
   394 length(List(King, Knight("foo"), Clown))
   361 def map_int_list(lst: List[Int], f: Int => Int): List[Int] = lst match {
   395 length(List(1, 2, 3, 4))
       
   396 
       
   397 def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
   362   case Nil => Nil
   398   case Nil => Nil
   363   case x::xs => f(x)::map_int_list(xs, f) 
   399   case x::xs => f(x)::map_int_list(xs, f) 
   364 }
   400 }
   365 
   401 
   366 map_int_list(List(1, 2, 3, 4), square)
   402 map_int_list(List(1, 2, 3, 4), square)
   369 // Remember?
   405 // Remember?
   370 def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ...
   406 def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ...
   371 
   407 
   372 
   408 
   373 
   409 
       
   410 
       
   411 
   374 // Cool Stuff
   412 // Cool Stuff
   375 //============
   413 //============
   376 
       
   377 
       
   378 
   414 
   379 
   415 
   380 // Implicits 
   416 // Implicits 
   381 //===========
   417 //===========
   382 //
   418 //
   408 case class ALT(r1: Rexp, r2: Rexp) extends Rexp     // alternative  r1 + r2
   444 case class ALT(r1: Rexp, r2: Rexp) extends Rexp     // alternative  r1 + r2
   409 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp     // sequence     r1 o r2  
   445 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp     // sequence     r1 o r2  
   410 case class STAR(r: Rexp) extends Rexp               // star         r*
   446 case class STAR(r: Rexp) extends Rexp               // star         r*
   411 
   447 
   412 
   448 
       
   449 
   413 // (ab)*
   450 // (ab)*
   414 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
   451 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
   415 
   452 
   416 
   453 
   417 // some convenience for typing in regular expressions
   454 // some convenience for typing in regular expressions
   425 }
   462 }
   426 implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList)
   463 implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList)
   427 
   464 
   428 
   465 
   429 val r1 = STAR("ab")
   466 val r1 = STAR("ab")
   430 val r2 = STAR("")
   467 val r2 = STAR(ALT("ab"))
   431 val r3 = STAR(ALT("ab", "baa baa black sheep"))
   468 val r3 = STAR(ALT("ab", "baa baa black sheep"))
   432 
   469 
   433 implicit def RexpOps (r: Rexp) = new {
   470 implicit def RexpOps (r: Rexp) = new {
   434   def | (s: Rexp) = ALT(r, s)
   471   def | (s: Rexp) = ALT(r, s)
   435   def % = STAR(r)
   472   def % = STAR(r)
   460 
   497 
   461 // Make your variables immutable, unless there's a good 
   498 // Make your variables immutable, unless there's a good 
   462 // reason not to.
   499 // reason not to.
   463 
   500 
   464 // You can be productive on Day 1, but the language is deep.
   501 // You can be productive on Day 1, but the language is deep.
   465 
   502 //
   466 // I like best about Scala that it lets me write
   503 // http://scalapuzzlers.com
       
   504 //
       
   505 // http://www.latkin.org/blog/2017/05/02/when-the-scala-compiler-doesnt-help/
       
   506 
       
   507 List(1, 2, 3) contains "your mom"
       
   508 
       
   509 // I like best about Scala that it lets me often write
   467 // concise, readable code.
   510 // concise, readable code.
   468 
   511