| author | Christian Urban <urbanc@in.tum.de> | 
| Fri, 05 Oct 2018 11:27:16 +0100 | |
| changeset 194 | 2e373c31baed | 
| parent 178 | 55367d499635 | 
| child 217 | bc92320b41d3 | 
| permissions | -rw-r--r-- | 
| 67 | 1 | // Scala Lecture 3 | 
| 2 | //================= | |
| 3 | ||
| 155 | 4 | // Pattern Matching | 
| 5 | //================== | |
| 6 | ||
| 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 | |
| 158 | 9 | // has it for many years. Other functional languages have it already for | 
| 10 | // decades. I think I would be really upset if a programming language | |
| 11 | // I have to use does not have pattern matching....its is just so | |
| 12 | // useful. ;o) | |
| 155 | 13 | |
| 14 | // The general schema: | |
| 15 | // | |
| 16 | //    expression match {
 | |
| 17 | // case pattern1 => expression1 | |
| 18 | // case pattern2 => expression2 | |
| 19 | // ... | |
| 20 | // case patternN => expressionN | |
| 21 | // } | |
| 22 | ||
| 23 | ||
| 24 | // remember | |
| 25 | val lst = List(None, Some(1), Some(2), None, Some(3)).flatten | |
| 26 | ||
| 27 | ||
| 28 | def my_flatten(xs: List[Option[Int]]): List[Int] = {
 | |
| 158 | 29 | if (xs == Nil) Nil | 
| 30 | else if (xs.head == None) my_flatten(xs.tail) | |
| 31 | else xs.head.get :: my_flatten(xs.tail) | |
| 155 | 32 | } | 
| 33 | ||
| 34 | ||
| 35 | ||
| 158 | 36 | val lst = List(None, Some(1), Some(2), None, Some(3)) | 
| 155 | 37 | |
| 38 | def my_flatten(lst: List[Option[Int]]): List[Int] = lst match {
 | |
| 39 | case Nil => Nil | |
| 40 | case None::xs => my_flatten(xs) | |
| 41 | case Some(n)::xs => n::my_flatten(xs) | |
| 42 | } | |
| 43 | ||
| 158 | 44 | my_flatten(lst) | 
| 45 | ||
| 46 | Nil == List() | |
| 47 | ||
| 155 | 48 | |
| 49 | // another example including a catch-all pattern | |
| 50 | def get_me_a_string(n: Int): String = n match {
 | |
| 51 | case 0 => "zero" | |
| 52 | case 1 => "one" | |
| 53 | case 2 => "two" | |
| 54 | case _ => "many" | |
| 55 | } | |
| 56 | ||
| 158 | 57 | get_me_a_string(10) | 
| 155 | 58 | |
| 59 | // you can also have cases combined | |
| 60 | def season(month: String) = month match {
 | |
| 61 | case "March" | "April" | "May" => "It's spring" | |
| 62 | case "June" | "July" | "August" => "It's summer" | |
| 63 | case "September" | "October" | "November" => "It's autumn" | |
| 64 | case "December" | "January" | "February" => "It's winter" | |
| 65 | } | |
| 66 | ||
| 67 | println(season("November"))
 | |
| 68 | ||
| 69 | // What happens if no case matches? | |
| 70 | ||
| 71 | println(season("foobar"))
 | |
| 72 | ||
| 73 | ||
| 158 | 74 | // we can also match more complicated pattern | 
| 75 | // | |
| 76 | // let's look at the Collatz function on binary strings | |
| 155 | 77 | |
| 78 | // adding two binary strings in a very, very lazy manner | |
| 152 | 79 | |
| 80 | def badd(s1: String, s2: String) : String = | |
| 81 | (BigInt(s1, 2) + BigInt(s2, 2)).toString(2) | |
| 82 | ||
| 83 | ||
| 158 | 84 | "111".dropRight(1) | 
| 85 | "111".last | |
| 152 | 86 | |
| 87 | def bcollatz(s: String) : Long = (s.dropRight(1), s.last) match {
 | |
| 158 | 88 |   case ("", '1') => 1                               // we reached 1
 | 
| 89 | case (rest, '0') => 1 + bcollatz(rest) | |
| 90 | // even number => divide by two | |
| 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 | |
| 152 | 94 | } | 
| 95 | ||
| 158 | 96 | bcollatz(6.toBinaryString) | 
| 152 | 97 | bcollatz(837799.toBinaryString) | 
| 98 | bcollatz(100000000000000000L.toBinaryString) | |
| 99 | bcollatz(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))
 | |
| 100 | ||
| 155 | 101 | |
| 102 | ||
| 103 | ||
| 104 | // User-defined Datatypes | |
| 105 | //======================== | |
| 106 | ||
| 107 | abstract class Colour | |
| 158 | 108 | case object Red extends Colour | 
| 109 | case object Green extends Colour | |
| 110 | case object Blue extends Colour | |
| 155 | 111 | |
| 112 | def fav_colour(c: Colour) : Boolean = c match {
 | |
| 158 | 113 | case Red => false | 
| 114 | case Green => true | |
| 115 | case Blue => false | |
| 152 | 116 | } | 
| 117 | ||
| 158 | 118 | fav_colour(Green) | 
| 119 | ||
| 152 | 120 | |
| 155 | 121 | // actually colors can be written with "object", | 
| 122 | // because they do not take any arguments | |
| 152 | 123 | |
| 178 | 124 | abstract class Day | 
| 125 | case object Monday extends Day | |
| 126 | case object Tuesday extends Day | |
| 127 | case object Wednesday extends Day | |
| 128 | case object Thursday extends Day | |
| 129 | case object Friday extends Day | |
| 130 | case object Saturday extends Day | |
| 131 | case object Sunday extends Day | |
| 132 | ||
| 194 | 133 | abstract class Suit | 
| 134 | case object Spades extends Suit | |
| 135 | case object Hearts extends Suit | |
| 136 | case object Diamonds extends Suit | |
| 137 | case object Clubs extends Suit | |
| 138 | ||
| 139 | //define function for colour of suits | |
| 140 | ||
| 141 | abstract class Rank | |
| 142 | case class Ace extends Rank | |
| 143 | case class King extends Rank | |
| 144 | case class Queen extends Rank | |
| 145 | case class Jack extends Rank | |
| 146 | case class Num(n: Int) extends Rank | |
| 147 | ||
| 148 | //define functions for beats | |
| 149 | //beats Ace _ => true | |
| 150 | //beats _ Acs => false | |
| 178 | 151 | |
| 152 | 152 | |
| 158 | 153 | // ... a bit more useful: Roman Numerals | 
| 67 | 154 | |
| 153 | 155 | abstract class RomanDigit | 
| 156 | case object I extends RomanDigit | |
| 157 | case object V extends RomanDigit | |
| 158 | case object X extends RomanDigit | |
| 159 | case object L extends RomanDigit | |
| 160 | case object C extends RomanDigit | |
| 161 | case object D extends RomanDigit | |
| 162 | case object M extends RomanDigit | |
| 163 | ||
| 164 | type RomanNumeral = List[RomanDigit] | |
| 67 | 165 | |
| 153 | 166 | def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
 | 
| 167 | case Nil => 0 | |
| 168 | case M::r => 1000 + RomanNumeral2Int(r) | |
| 169 | case C::M::r => 900 + RomanNumeral2Int(r) | |
| 170 | case D::r => 500 + RomanNumeral2Int(r) | |
| 171 | case C::D::r => 400 + RomanNumeral2Int(r) | |
| 172 | case C::r => 100 + RomanNumeral2Int(r) | |
| 173 | case X::C::r => 90 + RomanNumeral2Int(r) | |
| 174 | case L::r => 50 + RomanNumeral2Int(r) | |
| 175 | case X::L::r => 40 + RomanNumeral2Int(r) | |
| 176 | case X::r => 10 + RomanNumeral2Int(r) | |
| 177 | case I::X::r => 9 + RomanNumeral2Int(r) | |
| 178 | case V::r => 5 + RomanNumeral2Int(r) | |
| 179 | case I::V::r => 4 + RomanNumeral2Int(r) | |
| 180 | case I::r => 1 + RomanNumeral2Int(r) | |
| 67 | 181 | } | 
| 182 | ||
| 153 | 183 | RomanNumeral2Int(List(I,V)) // 4 | 
| 158 | 184 | RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid Roman number) | 
| 153 | 185 | RomanNumeral2Int(List(V,I)) // 6 | 
| 186 | RomanNumeral2Int(List(I,X)) // 9 | |
| 187 | RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 | |
| 188 | RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017 | |
| 67 | 189 | |
| 190 | ||
| 155 | 191 | |
| 192 | // another example | |
| 193 | //================= | |
| 194 | ||
| 158 | 195 | // Once upon a time, in a complete fictional country there were Persons... | 
| 67 | 196 | |
| 155 | 197 | abstract class Person | 
| 158 | 198 | case object King extends Person | 
| 155 | 199 | case class Peer(deg: String, terr: String, succ: Int) extends Person | 
| 200 | case class Knight(name: String) extends Person | |
| 201 | case class Peasant(name: String) extends Person | |
| 158 | 202 | case object Clown extends Person | 
| 155 | 203 | |
| 204 | def title(p: Person): String = p match {
 | |
| 158 | 205 | case King => "His Majesty the King" | 
| 155 | 206 |   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
 | 
| 207 |   case Knight(name) => s"Sir ${name}"
 | |
| 208 | case Peasant(name) => name | |
| 158 | 209 | case Clown => "My name is Boris Johnson" | 
| 210 | ||
| 67 | 211 | } | 
| 212 | ||
| 158 | 213 | title(Clown) | 
| 214 | ||
| 215 | ||
| 67 | 216 | |
| 155 | 217 | def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
 | 
| 158 | 218 | case (King, _) => true | 
| 155 | 219 | case (Peer(_,_,_), Knight(_)) => true | 
| 220 | case (Peer(_,_,_), Peasant(_)) => true | |
| 158 | 221 | case (Peer(_,_,_), Clown) => true | 
| 155 | 222 | case (Knight(_), Peasant(_)) => true | 
| 158 | 223 | case (Knight(_), Clown) => true | 
| 224 | case (Clown, Peasant(_)) => true | |
| 155 | 225 | case _ => false | 
| 226 | } | |
| 227 | ||
| 228 | val people = List(Knight("David"), 
 | |
| 229 |                   Peer("Duke", "Norfolk", 84), 
 | |
| 230 |                   Peasant("Christian"), 
 | |
| 158 | 231 | King, | 
| 232 | Clown) | |
| 155 | 233 | |
| 234 | println(people.sortWith(superior(_, _)).mkString(", "))
 | |
| 67 | 235 | |
| 236 | ||
| 155 | 237 | |
| 238 | ||
| 239 | // Tail recursion | |
| 240 | //================ | |
| 72 | 241 | |
| 67 | 242 | |
| 243 | def fact(n: Long): Long = | |
| 244 | if (n == 0) 1 else n * fact(n - 1) | |
| 245 | ||
| 155 | 246 | fact(10) //ok | 
| 247 | fact(10000) // produces a stackoverflow | |
| 248 | ||
| 249 | def factT(n: BigInt, acc: BigInt): BigInt = | |
| 250 | if (n == 0) acc else factT(n - 1, n * acc) | |
| 251 | ||
| 158 | 252 | factT(10, 1) | 
| 155 | 253 | factT(100000, 1) | 
| 254 | ||
| 255 | // there is a flag for ensuring a function is tail recursive | |
| 256 | import scala.annotation.tailrec | |
| 67 | 257 | |
| 72 | 258 | @tailrec | 
| 67 | 259 | def factT(n: BigInt, acc: BigInt): BigInt = | 
| 260 | if (n == 0) acc else factT(n - 1, n * acc) | |
| 261 | ||
| 262 | ||
| 263 | ||
| 155 | 264 | // for tail-recursive functions the Scala compiler | 
| 71 | 265 | // generates loop-like code, which does not need | 
| 67 | 266 | // to allocate stack-space in each recursive | 
| 155 | 267 | // call; Scala can do this only for tail-recursive | 
| 67 | 268 | // functions | 
| 269 | ||
| 155 | 270 | |
| 271 | ||
| 272 | // sudoku again | |
| 273 | ||
| 274 | val game0 = """.14.6.3.. | |
| 275 | |62...4..9 | |
| 276 | |.8..5.6.. | |
| 277 | |.6.2....3 | |
| 278 | |.7..1..5. | |
| 279 | |5....9.6. | |
| 280 | |..6.2..3. | |
| 281 | |1..5...92 | |
| 282 |               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
 | |
| 53 | 283 | |
| 155 | 284 | type Pos = (Int, Int) | 
| 285 | val EmptyValue = '.' | |
| 286 | val MaxValue = 9 | |
| 287 | ||
| 288 | val allValues = "123456789".toList | |
| 289 | val indexes = (0 to 8).toList | |
| 290 | ||
| 291 | ||
| 292 | def empty(game: String) = game.indexOf(EmptyValue) | |
| 293 | def isDone(game: String) = empty(game) == -1 | |
| 294 | def emptyPosition(game: String) = | |
| 295 | (empty(game) % MaxValue, empty(game) / MaxValue) | |
| 296 | ||
| 67 | 297 | |
| 155 | 298 | def get_row(game: String, y: Int) = | 
| 299 | indexes.map(col => game(y * MaxValue + col)) | |
| 300 | def get_col(game: String, x: Int) = | |
| 301 | indexes.map(row => game(x + row * MaxValue)) | |
| 302 | ||
| 303 | def get_box(game: String, pos: Pos): List[Char] = {
 | |
| 304 | def base(p: Int): Int = (p / 3) * 3 | |
| 305 | val x0 = base(pos._1) | |
| 306 | val y0 = base(pos._2) | |
| 307 | val ys = (y0 until y0 + 3).toList | |
| 308 | (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) | |
| 309 | } | |
| 310 | ||
| 311 | // this is not mutable!! | |
| 312 | def update(game: String, pos: Int, value: Char): String = | |
| 313 | game.updated(pos, value) | |
| 314 | ||
| 315 | def toAvoid(game: String, pos: Pos): List[Char] = | |
| 316 | (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) | |
| 317 | ||
| 318 | def candidates(game: String, pos: Pos): List[Char] = | |
| 319 | allValues.diff(toAvoid(game,pos)) | |
| 320 | ||
| 321 | //candidates(game0, (0,0)) | |
| 322 | ||
| 323 | def pretty(game: String): String = | |
| 324 | "\n" + (game sliding (MaxValue, MaxValue) mkString "\n") | |
| 325 | ||
| 158 | 326 | ///////////////////// | 
| 155 | 327 | // not tail recursive | 
| 328 | def search(game: String): List[String] = {
 | |
| 329 | if (isDone(game)) List(game) | |
| 330 |   else {
 | |
| 331 | val cs = candidates(game, emptyPosition(game)) | |
| 332 | cs.map(c => search(update(game, empty(game), c))).toList.flatten | |
| 67 | 333 | } | 
| 334 | } | |
| 335 | ||
| 155 | 336 | // tail recursive version that searches | 
| 158 | 337 | // for all solutions | 
| 338 | ||
| 155 | 339 | def searchT(games: List[String], sols: List[String]): List[String] = games match {
 | 
| 340 | case Nil => sols | |
| 341 |   case game::rest => {
 | |
| 342 | if (isDone(game)) searchT(rest, game::sols) | |
| 343 |     else {
 | |
| 344 | val cs = candidates(game, emptyPosition(game)) | |
| 345 | searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols) | |
| 346 | } | |
| 347 | } | |
| 67 | 348 | } | 
| 349 | ||
| 158 | 350 | searchT(List(game3), List()).map(pretty) | 
| 351 | ||
| 352 | ||
| 155 | 353 | // tail recursive version that searches | 
| 354 | // for a single solution | |
| 158 | 355 | |
| 155 | 356 | def search1T(games: List[String]): Option[String] = games match {
 | 
| 67 | 357 | case Nil => None | 
| 155 | 358 |   case game::rest => {
 | 
| 359 | if (isDone(game)) Some(game) | |
| 360 |     else {
 | |
| 361 | val cs = candidates(game, emptyPosition(game)) | |
| 362 | search1T(cs.map(c => update(game, empty(game), c)) ::: rest) | |
| 363 | } | |
| 364 | } | |
| 67 | 365 | } | 
| 366 | ||
| 158 | 367 | search1T(List(game3)).map(pretty) | 
| 368 | ||
| 155 | 369 | // game with multiple solutions | 
| 370 | val game3 = """.8...9743 | |
| 371 | |.5...8.1. | |
| 372 | |.1....... | |
| 373 | |8....5... | |
| 374 | |...8.4... | |
| 375 | |...3....6 | |
| 376 | |.......7. | |
| 377 | |.3.5...8. | |
| 378 |               |9724...5.""".stripMargin.replaceAll("\\n", "")
 | |
| 379 | ||
| 158 | 380 | searchT(List(game3), Nil).map(pretty) | 
| 155 | 381 | search1T(List(game3)).map(pretty) | 
| 67 | 382 | |
| 77 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 383 | // Moral: Whenever a recursive function is resource-critical | 
| 158 | 384 | // (i.e. works with large recursion depth), then you need to | 
| 77 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 385 | // write it in tail-recursive fashion. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 386 | // | 
| 155 | 387 | // Unfortuantely, Scala because of current limitations in | 
| 388 | // the JVM is not as clever as other functional languages. It can | |
| 77 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 389 | // only optimise "self-tail calls". This excludes the cases of | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 390 | // multiple functions making tail calls to each other. Well, | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 391 | // nothing is perfect. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 392 | |
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 393 | |
| 67 | 394 | |
| 395 | ||
| 71 | 396 | // Polymorphic Types | 
| 397 | //=================== | |
| 398 | ||
| 72 | 399 | // You do not want to write functions like contains, first | 
| 71 | 400 | // and so on for every type of lists. | 
| 401 | ||
| 67 | 402 | |
| 72 | 403 | def length_string_list(lst: List[String]): Int = lst match {
 | 
| 67 | 404 | case Nil => 0 | 
| 72 | 405 | case x::xs => 1 + length_string_list(xs) | 
| 67 | 406 | } | 
| 407 | ||
| 158 | 408 | def length_int_list(lst: List[Int]): Int = lst match {
 | 
| 409 | case Nil => 0 | |
| 410 | case x::xs => 1 + length_int_list(xs) | |
| 411 | } | |
| 67 | 412 | |
| 158 | 413 | length_string_list(List("1", "2", "3", "4"))
 | 
| 414 | length_int_list(List(1, 2, 3, 4)) | |
| 67 | 415 | |
| 158 | 416 | //----- | 
| 67 | 417 | def length[A](lst: List[A]): Int = lst match {
 | 
| 418 | case Nil => 0 | |
| 419 | case x::xs => 1 + length(xs) | |
| 420 | } | |
| 158 | 421 | length(List("1", "2", "3", "4"))
 | 
| 422 | length(List(King, Knight("foo"), Clown))
 | |
| 423 | length(List(1, 2, 3, 4)) | |
| 53 | 424 | |
| 158 | 425 | def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
 | 
| 67 | 426 | case Nil => Nil | 
| 427 | case x::xs => f(x)::map_int_list(xs, f) | |
| 428 | } | |
| 429 | ||
| 430 | map_int_list(List(1, 2, 3, 4), square) | |
| 431 | ||
| 432 | ||
| 433 | // Remember? | |
| 434 | def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ... | |
| 435 | ||
| 436 | ||
| 437 | ||
| 158 | 438 | |
| 439 | ||
| 155 | 440 | // Cool Stuff | 
| 441 | //============ | |
| 72 | 442 | |
| 155 | 443 | |
| 444 | // Implicits | |
| 445 | //=========== | |
| 446 | // | |
| 447 | // For example adding your own methods to Strings: | |
| 448 | // Imagine you want to increment strings, like | |
| 449 | // | |
| 450 | // "HAL".increment | |
| 451 | // | |
| 452 | // you can avoid ugly fudges, like a MyString, by | |
| 453 | // using implicit conversions. | |
| 67 | 454 | |
| 455 | ||
| 155 | 456 | implicit class MyString(s: String) {
 | 
| 457 | def increment = for (c <- s) yield (c + 1).toChar | |
| 67 | 458 | } | 
| 459 | ||
| 155 | 460 | "HAL".increment | 
| 67 | 461 | |
| 53 | 462 | |
| 463 | ||
| 464 | ||
| 71 | 465 | // Regular expressions - the power of DSLs in Scala | 
| 466 | //================================================== | |
| 67 | 467 | |
| 468 | abstract class Rexp | |
| 155 | 469 | case object ZERO extends Rexp // nothing | 
| 470 | case object ONE extends Rexp // the empty string | |
| 471 | case class CHAR(c: Char) extends Rexp // a character c | |
| 71 | 472 | case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2 | 
| 155 | 473 | case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 o r2 | 
| 71 | 474 | case class STAR(r: Rexp) extends Rexp // star r* | 
| 67 | 475 | |
| 476 | ||
| 158 | 477 | |
| 67 | 478 | // (ab)* | 
| 72 | 479 | val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
 | 
| 67 | 480 | |
| 481 | ||
| 482 | // some convenience for typing in regular expressions | |
| 483 | import scala.language.implicitConversions | |
| 484 | import scala.language.reflectiveCalls | |
| 485 | ||
| 486 | def charlist2rexp(s: List[Char]): Rexp = s match {
 | |
| 487 | case Nil => ONE | |
| 488 | case c::Nil => CHAR(c) | |
| 489 | case c::s => SEQ(CHAR(c), charlist2rexp(s)) | |
| 490 | } | |
| 491 | implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList) | |
| 492 | ||
| 493 | ||
| 494 | val r1 = STAR("ab")
 | |
| 158 | 495 | val r2 = STAR(ALT("ab"))
 | 
| 72 | 496 | val r3 = STAR(ALT("ab", "baa baa black sheep"))
 | 
| 67 | 497 | |
| 498 | implicit def RexpOps (r: Rexp) = new {
 | |
| 499 | def | (s: Rexp) = ALT(r, s) | |
| 500 | def % = STAR(r) | |
| 501 | def ~ (s: Rexp) = SEQ(r, s) | |
| 502 | } | |
| 503 | ||
| 504 | implicit def stringOps (s: String) = new {
 | |
| 505 | def | (r: Rexp) = ALT(s, r) | |
| 506 | def | (r: String) = ALT(s, r) | |
| 507 | def % = STAR(s) | |
| 508 | def ~ (r: Rexp) = SEQ(s, r) | |
| 509 | def ~ (r: String) = SEQ(s, r) | |
| 510 | } | |
| 511 | ||
| 153 | 512 | //example regular expressions | 
| 67 | 513 | val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | 
| 514 | val sign = "+" | "-" | "" | |
| 515 | val number = sign ~ digit ~ digit.% | |
| 516 | ||
| 517 | ||
| 518 | ||
| 519 | ||
| 520 | ||
| 521 | // The End | |
| 522 | //========= | |
| 523 | ||
| 524 | // A function should do one thing, and only one thing. | |
| 525 | ||
| 526 | // Make your variables immutable, unless there's a good | |
| 527 | // reason not to. | |
| 528 | ||
| 529 | // You can be productive on Day 1, but the language is deep. | |
| 158 | 530 | // | 
| 531 | // http://scalapuzzlers.com | |
| 532 | // | |
| 533 | // http://www.latkin.org/blog/2017/05/02/when-the-scala-compiler-doesnt-help/ | |
| 67 | 534 | |
| 158 | 535 | List(1, 2, 3) contains "your mom" | 
| 536 | ||
| 537 | // I like best about Scala that it lets me often write | |
| 155 | 538 | // concise, readable code. | 
| 68 | 539 | |
| 170 | 540 | |
| 541 | ||
| 542 | // You can define your own while loop | |
| 543 | ||
| 544 | ||
| 545 | def my_while(condition: => Boolean)(block: => Unit): Unit = | |
| 546 |   if (condition) { block ; my_while(condition) { block } } else { }
 | |
| 547 | ||
| 548 | ||
| 549 | var x = 10 | |
| 550 | my_while (x > 0) { 
 | |
| 551 | println(s"$x") ; x = x - 1 | |
| 552 | } | |
| 553 | ||
| 554 | ||
| 555 | `symbol | |
| 556 | `symbol` |