| author | Christian Urban <urbanc@in.tum.de> | 
| Thu, 23 Nov 2017 10:56:47 +0000 | |
| changeset 153 | 316f9c6cc2ff | 
| parent 152 | 16dbc95d7d77 | 
| child 155 | eccf17f56922 | 
| permissions | -rw-r--r-- | 
| 67 | 1 | // Scala Lecture 3 | 
| 2 | //================= | |
| 3 | ||
| 152 | 4 | // adding two binary strings very, very lazy manner | 
| 5 | ||
| 6 | def badd(s1: String, s2: String) : String = | |
| 7 | (BigInt(s1, 2) + BigInt(s2, 2)).toString(2) | |
| 8 | ||
| 9 | ||
| 10 | // collatz function on binary numbers | |
| 11 | ||
| 12 | def bcollatz(s: String) : Long = (s.dropRight(1), s.last) match {
 | |
| 13 |   case ("", '1') => 1                                  // we reached 1
 | |
| 14 | case (rest, '0') => 1 + bcollatz(rest) // even number => divide by two | |
| 15 | case (rest, '1') => 1 + bcollatz(badd(s + '1', s)) // odd number => s + '1' is 2 * s + 1 | |
| 16 | // add another s gives 3 * s + 1 | |
| 17 | } | |
| 18 | ||
| 19 | bcollatz(9.toBinaryString) | |
| 20 | bcollatz(837799.toBinaryString) | |
| 21 | bcollatz(100000000000000000L.toBinaryString) | |
| 22 | bcollatz(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))
 | |
| 23 | ||
| 24 | def conv(c: Char) : Int = c match {
 | |
| 25 | case '0' => 0 | |
| 26 | case '1' => 1 | |
| 27 | } | |
| 28 | ||
| 29 | def badds(s1: String, s2: String, carry: Int) : String = (s1, s2, carry) match {
 | |
| 30 |   case ("", "", 1) => "1"
 | |
| 31 |   case ("", "", 0) => ""
 | |
| 32 |   case (cs1, cs2, carry) => (conv(cs1.last) + conv(cs2.last) + carry) match {
 | |
| 33 | case 3 => badds(cs1.dropRight(1), cs2.dropRight(1), 1) + '1' | |
| 34 | case 2 => badds(cs1.dropRight(1), cs2.dropRight(1), 1) + '0' | |
| 35 | case 1 => badds(cs1.dropRight(1), cs2.dropRight(1), 0) + '1' | |
| 36 | case 0 => badds(cs1.dropRight(1), cs2.dropRight(1), 0) + '0' | |
| 37 | } | |
| 38 | } | |
| 39 | ||
| 40 | def bcollatz2(s: String) : Long = (s.dropRight(1), s.last) match {
 | |
| 41 |   case ("", '1') => 1                                          // we reached 1
 | |
| 42 | case (rest, '0') => 1 + bcollatz2(rest) // even number => divide by two | |
| 43 | case (rest, '1') => 1 + bcollatz2(badds(s + '1', '0' + s, 0)) // odd number => s + '1' is 2 * s + 1 | |
| 44 | // add another s gives 3 * s + 1 | |
| 45 | } | |
| 46 | ||
| 47 | bcollatz2(9.toBinaryString) | |
| 48 | bcollatz2(837799.toBinaryString) | |
| 49 | bcollatz2(100000000000000000L.toBinaryString) | |
| 50 | bcollatz2(BigInt("1000000000000000000000000000000000000000000000000000000000000000000000000000").toString(2))
 | |
| 51 | ||
| 52 | ||
| 67 | 53 | |
| 153 | 54 | // Roman Numerals | 
| 55 | abstract class RomanDigit | |
| 56 | case object I extends RomanDigit | |
| 57 | case object V extends RomanDigit | |
| 58 | case object X extends RomanDigit | |
| 59 | case object L extends RomanDigit | |
| 60 | case object C extends RomanDigit | |
| 61 | case object D extends RomanDigit | |
| 62 | case object M extends RomanDigit | |
| 63 | ||
| 64 | type RomanNumeral = List[RomanDigit] | |
| 67 | 65 | |
| 153 | 66 | def RomanNumeral2Int(rs: RomanNumeral): Int = rs match { 
 | 
| 67 | case Nil => 0 | |
| 68 | case M::r => 1000 + RomanNumeral2Int(r) | |
| 69 | case C::M::r => 900 + RomanNumeral2Int(r) | |
| 70 | case D::r => 500 + RomanNumeral2Int(r) | |
| 71 | case C::D::r => 400 + RomanNumeral2Int(r) | |
| 72 | case C::r => 100 + RomanNumeral2Int(r) | |
| 73 | case X::C::r => 90 + RomanNumeral2Int(r) | |
| 74 | case L::r => 50 + RomanNumeral2Int(r) | |
| 75 | case X::L::r => 40 + RomanNumeral2Int(r) | |
| 76 | case X::r => 10 + RomanNumeral2Int(r) | |
| 77 | case I::X::r => 9 + RomanNumeral2Int(r) | |
| 78 | case V::r => 5 + RomanNumeral2Int(r) | |
| 79 | case I::V::r => 4 + RomanNumeral2Int(r) | |
| 80 | case I::r => 1 + RomanNumeral2Int(r) | |
| 67 | 81 | } | 
| 82 | ||
| 153 | 83 | RomanNumeral2Int(List(I,I,I,I)) // 4 (invalid roman number) | 
| 84 | RomanNumeral2Int(List(I,V)) // 4 | |
| 85 | RomanNumeral2Int(List(V,I)) // 6 | |
| 86 | RomanNumeral2Int(List(I,X)) // 9 | |
| 87 | RomanNumeral2Int(List(M,C,M,L,X,X,I,X)) // 1979 | |
| 88 | RomanNumeral2Int(List(M,M,X,V,I,I)) // 2017 | |
| 67 | 89 | |
| 90 | ||
| 91 | // Tail recursion | |
| 92 | //================ | |
| 93 | ||
| 71 | 94 | def my_contains(elem: Int, lst: List[Int]): Boolean = lst match {
 | 
| 67 | 95 | case Nil => false | 
| 96 | case x::xs => | |
| 97 | if (x == elem) true else my_contains(elem, xs) | |
| 98 | } | |
| 99 | ||
| 100 | my_contains(4, List(1,2,3)) | |
| 101 | my_contains(2, List(1,2,3)) | |
| 102 | ||
| 103 | my_contains(1000000, (1 to 1000000).toList) | |
| 104 | my_contains(1000001, (1 to 1000000).toList) | |
| 105 | ||
| 106 | ||
| 71 | 107 | //factorial V0.1 | 
| 72 | 108 | import scala.annotation.tailrec | 
| 109 | ||
| 67 | 110 | |
| 111 | def fact(n: Long): Long = | |
| 112 | if (n == 0) 1 else n * fact(n - 1) | |
| 113 | ||
| 71 | 114 | fact(10000) // produces a stackoverflow | 
| 67 | 115 | |
| 72 | 116 | @tailrec | 
| 67 | 117 | def factT(n: BigInt, acc: BigInt): BigInt = | 
| 118 | if (n == 0) acc else factT(n - 1, n * acc) | |
| 119 | ||
| 120 | ||
| 72 | 121 | println(factT(10000, 1)) | 
| 67 | 122 | |
| 71 | 123 | // the functions my_contains and factT are tail-recursive | 
| 67 | 124 | // you can check this with | 
| 125 | ||
| 126 | import scala.annotation.tailrec | |
| 127 | ||
| 128 | // and the annotation @tailrec | |
| 129 | ||
| 71 | 130 | // for tail-recursive functions the scala compiler | 
| 131 | // generates loop-like code, which does not need | |
| 67 | 132 | // to allocate stack-space in each recursive | 
| 133 | // call; scala can do this only for tail-recursive | |
| 134 | // functions | |
| 135 | ||
| 136 | // consider the following "stupid" version of the | |
| 71 | 137 | // coin exchange problem: given some coins and a | 
| 138 | // total, what is the change can you get? | |
| 53 | 139 | |
| 71 | 140 | val coins = List(4,5,6,8,10,13,19,20,21,24,38,39,40) | 
| 67 | 141 | |
| 142 | def first_positive[B](lst: List[Int], f: Int => Option[B]): Option[B] = lst match {
 | |
| 143 | case Nil => None | |
| 144 | case x::xs => | |
| 145 | if (x <= 0) first_positive(xs, f) | |
| 146 |     else {
 | |
| 147 | val fx = f(x) | |
| 148 | if (fx.isDefined) fx else first_positive(xs, f) | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | ||
| 72 | 153 | import scala.annotation.tailrec | 
| 154 | ||
| 67 | 155 | def search(total: Int, coins: List[Int], cs: List[Int]): Option[List[Int]] = {
 | 
| 156 | if (total < cs.sum) None | |
| 157 | else if (cs.sum == total) Some(cs) | |
| 158 | else first_positive(coins, (c: Int) => search(total, coins, c::cs)) | |
| 159 | } | |
| 160 | ||
| 161 | search(11, coins, Nil) | |
| 162 | search(111, coins, Nil) | |
| 163 | search(111111, coins, Nil) | |
| 53 | 164 | |
| 67 | 165 | val junk_coins = List(4,-2,5,6,8,0,10,13,19,20,-3,21,24,38,39, 40) | 
| 166 | search(11, junk_coins, Nil) | |
| 167 | search(111, junk_coins, Nil) | |
| 168 | ||
| 169 | ||
| 170 | import scala.annotation.tailrec | |
| 171 | ||
| 172 | @tailrec | |
| 72 | 173 | def searchT(total: Int, coins: List[Int], | 
| 174 |             acc_cs: List[List[Int]]): Option[List[Int]] = acc_cs match {
 | |
| 67 | 175 | case Nil => None | 
| 176 | case x::xs => | |
| 71 | 177 | if (total < x.sum) searchT(total, coins, xs) | 
| 67 | 178 | else if (x.sum == total) Some(x) | 
| 71 | 179 | else searchT(total, coins, coins.filter(_ > 0).map(_::x) ::: xs) | 
| 67 | 180 | } | 
| 181 | ||
| 182 | val start_acc = coins.filter(_ > 0).map(List(_)) | |
| 71 | 183 | searchT(11, junk_coins, start_acc) | 
| 184 | searchT(111, junk_coins, start_acc) | |
| 185 | searchT(111111, junk_coins, start_acc) | |
| 67 | 186 | |
| 77 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 187 | // Moral: Whenever a recursive function is resource-critical | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 188 | // (i.e. works with large recursion depths), then you need to | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 189 | // write it in tail-recursive fashion. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 190 | // | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 191 | // Unfortuantely, the Scala is because of current limitations in | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 192 | // the JVM not as clever as other functional languages. It can | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 193 | // 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 | 194 | // 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 | 195 | // nothing is perfect. | 
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 196 | |
| 
3cbe3d90b77f
updated
 Christian Urban <christian dot urban at kcl dot ac dot uk> parents: 
73diff
changeset | 197 | |
| 67 | 198 | |
| 199 | ||
| 71 | 200 | // Polymorphic Types | 
| 201 | //=================== | |
| 202 | ||
| 72 | 203 | // You do not want to write functions like contains, first | 
| 71 | 204 | // and so on for every type of lists. | 
| 205 | ||
| 67 | 206 | |
| 72 | 207 | def length_string_list(lst: List[String]): Int = lst match {
 | 
| 67 | 208 | case Nil => 0 | 
| 72 | 209 | case x::xs => 1 + length_string_list(xs) | 
| 67 | 210 | } | 
| 211 | ||
| 72 | 212 | length_string_list(List("1", "2", "3", "4"))
 | 
| 67 | 213 | |
| 214 | ||
| 215 | def length[A](lst: List[A]): Int = lst match {
 | |
| 216 | case Nil => 0 | |
| 217 | case x::xs => 1 + length(xs) | |
| 218 | } | |
| 219 | ||
| 53 | 220 | |
| 67 | 221 | def map_int_list(lst: List[Int], f: Int => Int): List[Int] = lst match {
 | 
| 222 | case Nil => Nil | |
| 223 | case x::xs => f(x)::map_int_list(xs, f) | |
| 224 | } | |
| 225 | ||
| 226 | map_int_list(List(1, 2, 3, 4), square) | |
| 227 | ||
| 228 | ||
| 229 | // Remember? | |
| 230 | def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ... | |
| 231 | ||
| 232 | ||
| 233 | // polymorphic classes | |
| 234 | //(trees with some content) | |
| 235 | ||
| 236 | abstract class Tree[+A] | |
| 237 | case class Node[A](elem: A, left: Tree[A], right: Tree[A]) extends Tree[A] | |
| 238 | case object Leaf extends Tree[Nothing] | |
| 239 | ||
| 72 | 240 | val t0 = Node('4', Node('2', Leaf, Leaf), Node('7', Leaf, Leaf))
 | 
| 241 | ||
| 67 | 242 | def insert[A](tr: Tree[A], n: A): Tree[A] = tr match {
 | 
| 243 | case Leaf => Node(n, Leaf, Leaf) | |
| 244 | case Node(m, left, right) => | |
| 245 | if (n == m) Node(m, left, right) | |
| 246 | else if (n < m) Node(m, insert(left, n), right) | |
| 247 | else Node(m, left, insert(right, n)) | |
| 248 | } | |
| 249 | ||
| 250 | ||
| 251 | // the A-type needs to be ordered | |
| 252 | ||
| 253 | abstract class Tree[+A <% Ordered[A]] | |
| 72 | 254 | case class Node[A <% Ordered[A]](elem: A, left: Tree[A], | 
| 255 | right: Tree[A]) extends Tree[A] | |
| 67 | 256 | case object Leaf extends Tree[Nothing] | 
| 257 | ||
| 258 | ||
| 259 | def insert[A <% Ordered[A]](tr: Tree[A], n: A): Tree[A] = tr match {
 | |
| 260 | case Leaf => Node(n, Leaf, Leaf) | |
| 261 | case Node(m, left, right) => | |
| 262 | if (n == m) Node(m, left, right) | |
| 263 | else if (n < m) Node(m, insert(left, n), right) | |
| 264 | else Node(m, left, insert(right, n)) | |
| 265 | } | |
| 266 | ||
| 267 | ||
| 268 | val t1 = Node(4, Node(2, Leaf, Leaf), Node(7, Leaf, Leaf)) | |
| 269 | insert(t1, 3) | |
| 270 | ||
| 271 | val t2 = Node('b', Node('a', Leaf, Leaf), Node('f', Leaf, Leaf))
 | |
| 272 | insert(t2, 'e') | |
| 53 | 273 | |
| 274 | ||
| 275 | ||
| 71 | 276 | // Regular expressions - the power of DSLs in Scala | 
| 277 | //================================================== | |
| 67 | 278 | |
| 279 | ||
| 280 | abstract class Rexp | |
| 281 | case object ZERO extends Rexp | |
| 282 | case object ONE extends Rexp | |
| 283 | case class CHAR(c: Char) extends Rexp | |
| 71 | 284 | case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2 | 
| 72 | 285 | case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 r2 | 
| 71 | 286 | case class STAR(r: Rexp) extends Rexp // star r* | 
| 67 | 287 | |
| 288 | ||
| 289 | // (ab)* | |
| 72 | 290 | val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
 | 
| 67 | 291 | |
| 292 | ||
| 293 | // some convenience for typing in regular expressions | |
| 294 | import scala.language.implicitConversions | |
| 295 | import scala.language.reflectiveCalls | |
| 296 | ||
| 297 | def charlist2rexp(s: List[Char]): Rexp = s match {
 | |
| 298 | case Nil => ONE | |
| 299 | case c::Nil => CHAR(c) | |
| 300 | case c::s => SEQ(CHAR(c), charlist2rexp(s)) | |
| 301 | } | |
| 302 | implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList) | |
| 303 | ||
| 304 | ||
| 305 | val r1 = STAR("ab")
 | |
| 306 | val r2 = STAR("")
 | |
| 72 | 307 | val r3 = STAR(ALT("ab", "baa baa black sheep"))
 | 
| 67 | 308 | |
| 309 | implicit def RexpOps (r: Rexp) = new {
 | |
| 310 | def | (s: Rexp) = ALT(r, s) | |
| 311 | def % = STAR(r) | |
| 312 | def ~ (s: Rexp) = SEQ(r, s) | |
| 313 | } | |
| 314 | ||
| 315 | implicit def stringOps (s: String) = new {
 | |
| 316 | def | (r: Rexp) = ALT(s, r) | |
| 317 | def | (r: String) = ALT(s, r) | |
| 318 | def % = STAR(s) | |
| 319 | def ~ (r: Rexp) = SEQ(s, r) | |
| 320 | def ~ (r: String) = SEQ(s, r) | |
| 321 | } | |
| 322 | ||
| 153 | 323 | //example regular expressions | 
| 67 | 324 | val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | 
| 325 | val sign = "+" | "-" | "" | |
| 326 | val number = sign ~ digit ~ digit.% | |
| 327 | ||
| 328 | ||
| 153 | 329 | //implement print_re | 
| 330 | ||
| 331 | ||
| 67 | 332 | |
| 333 | // Lazyness with style | |
| 334 | //===================== | |
| 335 | ||
| 336 | // The concept of lazy evaluation doesn’t really exist in | |
| 337 | // non-functional languages, but it is pretty easy to grasp. | |
| 338 | // Consider first | |
| 339 | ||
| 340 | def square(x: Int) = x * x | |
| 341 | ||
| 342 | square(42 + 8) | |
| 343 | ||
| 344 | // this is called strict evaluation | |
| 345 | ||
| 346 | ||
| 347 | def expensiveOperation(n: BigInt): Boolean = expensiveOperation(n + 1) | |
| 348 | val a = "foo" | |
| 72 | 349 | val b = "bar" | 
| 67 | 350 | |
| 351 | val test = if ((a == b) || expensiveOperation(0)) true else false | |
| 352 | ||
| 353 | // this is called lazy evaluation | |
| 354 | // you delay compuation until it is really | |
| 355 | // needed; once calculated though, does not | |
| 356 | // need to be re-calculated | |
| 357 | ||
| 358 | // a useful example is | |
| 359 | def time_needed[T](i: Int, code: => T) = {
 | |
| 360 | val start = System.nanoTime() | |
| 361 | for (j <- 1 to i) code | |
| 362 | val end = System.nanoTime() | |
| 363 | ((end - start) / i / 1.0e9) + " secs" | |
| 364 | } | |
| 365 | ||
| 366 | ||
| 367 | // streams (I do not care how many) | |
| 368 | // primes: 2, 3, 5, 7, 9, 11, 13 .... | |
| 369 | ||
| 370 | def generatePrimes (s: Stream[Int]): Stream[Int] = | |
| 371 | s.head #:: generatePrimes(s.tail filter (_ % s.head != 0)) | |
| 372 | ||
| 373 | val primes: Stream[Int] = generatePrimes(Stream.from(2)) | |
| 374 | ||
| 73 | 375 | primes.take(10).toList | 
| 376 | ||
| 67 | 377 | primes.filter(_ > 100).take(2000).toList | 
| 378 | ||
| 379 | time_needed(1, primes.filter(_ > 100).take(2000).toList) | |
| 380 | time_needed(1, primes.filter(_ > 100).take(2000).toList) | |
| 381 | ||
| 382 | ||
| 383 | ||
| 384 | // streams are useful for implementing search problems ;o) | |
| 385 | ||
| 386 | ||
| 387 | ||
| 388 | ||
| 389 | // The End | |
| 390 | //========= | |
| 391 | ||
| 392 | // A function should do one thing, and only one thing. | |
| 393 | ||
| 394 | // Make your variables immutable, unless there's a good | |
| 395 | // reason not to. | |
| 396 | ||
| 397 | // You can be productive on Day 1, but the language is deep. | |
| 398 | ||
| 68 | 399 | // I like best about Scala that it lets me write | 
| 67 | 400 | // concise, readable code | 
| 68 | 401 |