| author | pdated | 
| Sat, 16 Jun 2018 20:54:58 +0100 | |
| changeset 184 | 19b1e01054f6 | 
| parent 178 | 55367d499635 | 
| child 194 | 2e373c31baed | 
| 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 | ||
| 133 | ||
| 152 | 134 | |
| 158 | 135 | // ... a bit more useful: Roman Numerals | 
| 67 | 136 | |
| 153 | 137 | abstract class RomanDigit | 
| 138 | case object I extends RomanDigit | |
| 139 | case object V extends RomanDigit | |
| 140 | case object X extends RomanDigit | |
| 141 | case object L extends RomanDigit | |
| 142 | case object C extends RomanDigit | |
| 143 | case object D extends RomanDigit | |
| 144 | case object M extends RomanDigit | |
| 145 | ||
| 146 | type RomanNumeral = List[RomanDigit] | |
| 67 | 147 | |
| 153 | 148 | def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
 | 
| 149 | case Nil => 0 | |
| 150 | case M::r => 1000 + RomanNumeral2Int(r) | |
| 151 | case C::M::r => 900 + RomanNumeral2Int(r) | |
| 152 | case D::r => 500 + RomanNumeral2Int(r) | |
| 153 | case C::D::r => 400 + RomanNumeral2Int(r) | |
| 154 | case C::r => 100 + RomanNumeral2Int(r) | |
| 155 | case X::C::r => 90 + RomanNumeral2Int(r) | |
| 156 | case L::r => 50 + RomanNumeral2Int(r) | |
| 157 | case X::L::r => 40 + RomanNumeral2Int(r) | |
| 158 | case X::r => 10 + RomanNumeral2Int(r) | |
| 159 | case I::X::r => 9 + RomanNumeral2Int(r) | |
| 160 | case V::r => 5 + RomanNumeral2Int(r) | |
| 161 | case I::V::r => 4 + RomanNumeral2Int(r) | |
| 162 | case I::r => 1 + RomanNumeral2Int(r) | |
| 67 | 163 | } | 
| 164 | ||
| 153 | 165 | RomanNumeral2Int(List(I,V)) // 4 | 
| 158 | 166 | RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid Roman number) | 
| 153 | 167 | RomanNumeral2Int(List(V,I)) // 6 | 
| 168 | RomanNumeral2Int(List(I,X)) // 9 | |
| 169 | RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 | |
| 170 | RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017 | |
| 67 | 171 | |
| 172 | ||
| 155 | 173 | |
| 174 | // another example | |
| 175 | //================= | |
| 176 | ||
| 158 | 177 | // Once upon a time, in a complete fictional country there were Persons... | 
| 67 | 178 | |
| 155 | 179 | abstract class Person | 
| 158 | 180 | case object King extends Person | 
| 155 | 181 | case class Peer(deg: String, terr: String, succ: Int) extends Person | 
| 182 | case class Knight(name: String) extends Person | |
| 183 | case class Peasant(name: String) extends Person | |
| 158 | 184 | case object Clown extends Person | 
| 155 | 185 | |
| 186 | def title(p: Person): String = p match {
 | |
| 158 | 187 | case King => "His Majesty the King" | 
| 155 | 188 |   case Peer(deg, terr, _) => s"The ${deg} of ${terr}"
 | 
| 189 |   case Knight(name) => s"Sir ${name}"
 | |
| 190 | case Peasant(name) => name | |
| 158 | 191 | case Clown => "My name is Boris Johnson" | 
| 192 | ||
| 67 | 193 | } | 
| 194 | ||
| 158 | 195 | title(Clown) | 
| 196 | ||
| 197 | ||
| 67 | 198 | |
| 155 | 199 | def superior(p1: Person, p2: Person): Boolean = (p1, p2) match {
 | 
| 158 | 200 | case (King, _) => true | 
| 155 | 201 | case (Peer(_,_,_), Knight(_)) => true | 
| 202 | case (Peer(_,_,_), Peasant(_)) => true | |
| 158 | 203 | case (Peer(_,_,_), Clown) => true | 
| 155 | 204 | case (Knight(_), Peasant(_)) => true | 
| 158 | 205 | case (Knight(_), Clown) => true | 
| 206 | case (Clown, Peasant(_)) => true | |
| 155 | 207 | case _ => false | 
| 208 | } | |
| 209 | ||
| 210 | val people = List(Knight("David"), 
 | |
| 211 |                   Peer("Duke", "Norfolk", 84), 
 | |
| 212 |                   Peasant("Christian"), 
 | |
| 158 | 213 | King, | 
| 214 | Clown) | |
| 155 | 215 | |
| 216 | println(people.sortWith(superior(_, _)).mkString(", "))
 | |
| 67 | 217 | |
| 218 | ||
| 155 | 219 | |
| 220 | ||
| 221 | // Tail recursion | |
| 222 | //================ | |
| 72 | 223 | |
| 67 | 224 | |
| 225 | def fact(n: Long): Long = | |
| 226 | if (n == 0) 1 else n * fact(n - 1) | |
| 227 | ||
| 155 | 228 | fact(10) //ok | 
| 229 | fact(10000) // produces a stackoverflow | |
| 230 | ||
| 231 | def factT(n: BigInt, acc: BigInt): BigInt = | |
| 232 | if (n == 0) acc else factT(n - 1, n * acc) | |
| 233 | ||
| 158 | 234 | factT(10, 1) | 
| 155 | 235 | factT(100000, 1) | 
| 236 | ||
| 237 | // there is a flag for ensuring a function is tail recursive | |
| 238 | import scala.annotation.tailrec | |
| 67 | 239 | |
| 72 | 240 | @tailrec | 
| 67 | 241 | def factT(n: BigInt, acc: BigInt): BigInt = | 
| 242 | if (n == 0) acc else factT(n - 1, n * acc) | |
| 243 | ||
| 244 | ||
| 245 | ||
| 155 | 246 | // for tail-recursive functions the Scala compiler | 
| 71 | 247 | // generates loop-like code, which does not need | 
| 67 | 248 | // to allocate stack-space in each recursive | 
| 155 | 249 | // call; Scala can do this only for tail-recursive | 
| 67 | 250 | // functions | 
| 251 | ||
| 155 | 252 | |
| 253 | ||
| 254 | // sudoku again | |
| 255 | ||
| 256 | val game0 = """.14.6.3.. | |
| 257 | |62...4..9 | |
| 258 | |.8..5.6.. | |
| 259 | |.6.2....3 | |
| 260 | |.7..1..5. | |
| 261 | |5....9.6. | |
| 262 | |..6.2..3. | |
| 263 | |1..5...92 | |
| 264 |               |..7.9.41.""".stripMargin.replaceAll("\\n", "")
 | |
| 53 | 265 | |
| 155 | 266 | type Pos = (Int, Int) | 
| 267 | val EmptyValue = '.' | |
| 268 | val MaxValue = 9 | |
| 269 | ||
| 270 | val allValues = "123456789".toList | |
| 271 | val indexes = (0 to 8).toList | |
| 272 | ||
| 273 | ||
| 274 | def empty(game: String) = game.indexOf(EmptyValue) | |
| 275 | def isDone(game: String) = empty(game) == -1 | |
| 276 | def emptyPosition(game: String) = | |
| 277 | (empty(game) % MaxValue, empty(game) / MaxValue) | |
| 278 | ||
| 67 | 279 | |
| 155 | 280 | def get_row(game: String, y: Int) = | 
| 281 | indexes.map(col => game(y * MaxValue + col)) | |
| 282 | def get_col(game: String, x: Int) = | |
| 283 | indexes.map(row => game(x + row * MaxValue)) | |
| 284 | ||
| 285 | def get_box(game: String, pos: Pos): List[Char] = {
 | |
| 286 | def base(p: Int): Int = (p / 3) * 3 | |
| 287 | val x0 = base(pos._1) | |
| 288 | val y0 = base(pos._2) | |
| 289 | val ys = (y0 until y0 + 3).toList | |
| 290 | (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) | |
| 291 | } | |
| 292 | ||
| 293 | // this is not mutable!! | |
| 294 | def update(game: String, pos: Int, value: Char): String = | |
| 295 | game.updated(pos, value) | |
| 296 | ||
| 297 | def toAvoid(game: String, pos: Pos): List[Char] = | |
| 298 | (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) | |
| 299 | ||
| 300 | def candidates(game: String, pos: Pos): List[Char] = | |
| 301 | allValues.diff(toAvoid(game,pos)) | |
| 302 | ||
| 303 | //candidates(game0, (0,0)) | |
| 304 | ||
| 305 | def pretty(game: String): String = | |
| 306 | "\n" + (game sliding (MaxValue, MaxValue) mkString "\n") | |
| 307 | ||
| 158 | 308 | ///////////////////// | 
| 155 | 309 | // not tail recursive | 
| 310 | def search(game: String): List[String] = {
 | |
| 311 | if (isDone(game)) List(game) | |
| 312 |   else {
 | |
| 313 | val cs = candidates(game, emptyPosition(game)) | |
| 314 | cs.map(c => search(update(game, empty(game), c))).toList.flatten | |
| 67 | 315 | } | 
| 316 | } | |
| 317 | ||
| 155 | 318 | // tail recursive version that searches | 
| 158 | 319 | // for all solutions | 
| 320 | ||
| 155 | 321 | def searchT(games: List[String], sols: List[String]): List[String] = games match {
 | 
| 322 | case Nil => sols | |
| 323 |   case game::rest => {
 | |
| 324 | if (isDone(game)) searchT(rest, game::sols) | |
| 325 |     else {
 | |
| 326 | val cs = candidates(game, emptyPosition(game)) | |
| 327 | searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols) | |
| 328 | } | |
| 329 | } | |
| 67 | 330 | } | 
| 331 | ||
| 158 | 332 | searchT(List(game3), List()).map(pretty) | 
| 333 | ||
| 334 | ||
| 155 | 335 | // tail recursive version that searches | 
| 336 | // for a single solution | |
| 158 | 337 | |
| 155 | 338 | def search1T(games: List[String]): Option[String] = games match {
 | 
| 67 | 339 | case Nil => None | 
| 155 | 340 |   case game::rest => {
 | 
| 341 | if (isDone(game)) Some(game) | |
| 342 |     else {
 | |
| 343 | val cs = candidates(game, emptyPosition(game)) | |
| 344 | search1T(cs.map(c => update(game, empty(game), c)) ::: rest) | |
| 345 | } | |
| 346 | } | |
| 67 | 347 | } | 
| 348 | ||
| 158 | 349 | search1T(List(game3)).map(pretty) | 
| 350 | ||
| 155 | 351 | // game with multiple solutions | 
| 352 | val game3 = """.8...9743 | |
| 353 | |.5...8.1. | |
| 354 | |.1....... | |
| 355 | |8....5... | |
| 356 | |...8.4... | |
| 357 | |...3....6 | |
| 358 | |.......7. | |
| 359 | |.3.5...8. | |
| 360 |               |9724...5.""".stripMargin.replaceAll("\\n", "")
 | |
| 361 | ||
| 158 | 362 | searchT(List(game3), Nil).map(pretty) | 
| 155 | 363 | search1T(List(game3)).map(pretty) | 
| 67 | 364 | |
| 77 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 365 | // Moral: Whenever a recursive function is resource-critical | 
| 158 | 366 | // (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 | 367 | // write it in tail-recursive fashion. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 368 | // | 
| 155 | 369 | // Unfortuantely, Scala because of current limitations in | 
| 370 | // 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 | 371 | // 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 | 372 | // 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 | 373 | // nothing is perfect. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 374 | |
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 375 | |
| 67 | 376 | |
| 377 | ||
| 71 | 378 | // Polymorphic Types | 
| 379 | //=================== | |
| 380 | ||
| 72 | 381 | // You do not want to write functions like contains, first | 
| 71 | 382 | // and so on for every type of lists. | 
| 383 | ||
| 67 | 384 | |
| 72 | 385 | def length_string_list(lst: List[String]): Int = lst match {
 | 
| 67 | 386 | case Nil => 0 | 
| 72 | 387 | case x::xs => 1 + length_string_list(xs) | 
| 67 | 388 | } | 
| 389 | ||
| 158 | 390 | def length_int_list(lst: List[Int]): Int = lst match {
 | 
| 391 | case Nil => 0 | |
| 392 | case x::xs => 1 + length_int_list(xs) | |
| 393 | } | |
| 67 | 394 | |
| 158 | 395 | length_string_list(List("1", "2", "3", "4"))
 | 
| 396 | length_int_list(List(1, 2, 3, 4)) | |
| 67 | 397 | |
| 158 | 398 | //----- | 
| 67 | 399 | def length[A](lst: List[A]): Int = lst match {
 | 
| 400 | case Nil => 0 | |
| 401 | case x::xs => 1 + length(xs) | |
| 402 | } | |
| 158 | 403 | length(List("1", "2", "3", "4"))
 | 
| 404 | length(List(King, Knight("foo"), Clown))
 | |
| 405 | length(List(1, 2, 3, 4)) | |
| 53 | 406 | |
| 158 | 407 | def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
 | 
| 67 | 408 | case Nil => Nil | 
| 409 | case x::xs => f(x)::map_int_list(xs, f) | |
| 410 | } | |
| 411 | ||
| 412 | map_int_list(List(1, 2, 3, 4), square) | |
| 413 | ||
| 414 | ||
| 415 | // Remember? | |
| 416 | def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ... | |
| 417 | ||
| 418 | ||
| 419 | ||
| 158 | 420 | |
| 421 | ||
| 155 | 422 | // Cool Stuff | 
| 423 | //============ | |
| 72 | 424 | |
| 155 | 425 | |
| 426 | // Implicits | |
| 427 | //=========== | |
| 428 | // | |
| 429 | // For example adding your own methods to Strings: | |
| 430 | // Imagine you want to increment strings, like | |
| 431 | // | |
| 432 | // "HAL".increment | |
| 433 | // | |
| 434 | // you can avoid ugly fudges, like a MyString, by | |
| 435 | // using implicit conversions. | |
| 67 | 436 | |
| 437 | ||
| 155 | 438 | implicit class MyString(s: String) {
 | 
| 439 | def increment = for (c <- s) yield (c + 1).toChar | |
| 67 | 440 | } | 
| 441 | ||
| 155 | 442 | "HAL".increment | 
| 67 | 443 | |
| 53 | 444 | |
| 445 | ||
| 446 | ||
| 71 | 447 | // Regular expressions - the power of DSLs in Scala | 
| 448 | //================================================== | |
| 67 | 449 | |
| 450 | abstract class Rexp | |
| 155 | 451 | case object ZERO extends Rexp // nothing | 
| 452 | case object ONE extends Rexp // the empty string | |
| 453 | case class CHAR(c: Char) extends Rexp // a character c | |
| 71 | 454 | case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2 | 
| 155 | 455 | case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 o r2 | 
| 71 | 456 | case class STAR(r: Rexp) extends Rexp // star r* | 
| 67 | 457 | |
| 458 | ||
| 158 | 459 | |
| 67 | 460 | // (ab)* | 
| 72 | 461 | val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
 | 
| 67 | 462 | |
| 463 | ||
| 464 | // some convenience for typing in regular expressions | |
| 465 | import scala.language.implicitConversions | |
| 466 | import scala.language.reflectiveCalls | |
| 467 | ||
| 468 | def charlist2rexp(s: List[Char]): Rexp = s match {
 | |
| 469 | case Nil => ONE | |
| 470 | case c::Nil => CHAR(c) | |
| 471 | case c::s => SEQ(CHAR(c), charlist2rexp(s)) | |
| 472 | } | |
| 473 | implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList) | |
| 474 | ||
| 475 | ||
| 476 | val r1 = STAR("ab")
 | |
| 158 | 477 | val r2 = STAR(ALT("ab"))
 | 
| 72 | 478 | val r3 = STAR(ALT("ab", "baa baa black sheep"))
 | 
| 67 | 479 | |
| 480 | implicit def RexpOps (r: Rexp) = new {
 | |
| 481 | def | (s: Rexp) = ALT(r, s) | |
| 482 | def % = STAR(r) | |
| 483 | def ~ (s: Rexp) = SEQ(r, s) | |
| 484 | } | |
| 485 | ||
| 486 | implicit def stringOps (s: String) = new {
 | |
| 487 | def | (r: Rexp) = ALT(s, r) | |
| 488 | def | (r: String) = ALT(s, r) | |
| 489 | def % = STAR(s) | |
| 490 | def ~ (r: Rexp) = SEQ(s, r) | |
| 491 | def ~ (r: String) = SEQ(s, r) | |
| 492 | } | |
| 493 | ||
| 153 | 494 | //example regular expressions | 
| 67 | 495 | val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | 
| 496 | val sign = "+" | "-" | "" | |
| 497 | val number = sign ~ digit ~ digit.% | |
| 498 | ||
| 499 | ||
| 500 | ||
| 501 | ||
| 502 | ||
| 503 | // The End | |
| 504 | //========= | |
| 505 | ||
| 506 | // A function should do one thing, and only one thing. | |
| 507 | ||
| 508 | // Make your variables immutable, unless there's a good | |
| 509 | // reason not to. | |
| 510 | ||
| 511 | // You can be productive on Day 1, but the language is deep. | |
| 158 | 512 | // | 
| 513 | // http://scalapuzzlers.com | |
| 514 | // | |
| 515 | // http://www.latkin.org/blog/2017/05/02/when-the-scala-compiler-doesnt-help/ | |
| 67 | 516 | |
| 158 | 517 | List(1, 2, 3) contains "your mom" | 
| 518 | ||
| 519 | // I like best about Scala that it lets me often write | |
| 155 | 520 | // concise, readable code. | 
| 68 | 521 | |
| 170 | 522 | |
| 523 | ||
| 524 | // You can define your own while loop | |
| 525 | ||
| 526 | ||
| 527 | def my_while(condition: => Boolean)(block: => Unit): Unit = | |
| 528 |   if (condition) { block ; my_while(condition) { block } } else { }
 | |
| 529 | ||
| 530 | ||
| 531 | var x = 10 | |
| 532 | my_while (x > 0) { 
 | |
| 533 | println(s"$x") ; x = x - 1 | |
| 534 | } | |
| 535 | ||
| 536 | ||
| 537 | `symbol | |
| 538 | `symbol` |