1 // Scala Lecture 4 |
1 // Scala Lecture 4 |
2 //================= |
2 //================= |
|
3 |
|
4 |
|
5 // expressions (essentially trees) |
|
6 |
|
7 abstract class Exp |
|
8 case class N(n: Int) extends Exp // for numbers |
|
9 case class Plus(e1: Exp, e2: Exp) extends Exp |
|
10 case class Times(e1: Exp, e2: Exp) extends Exp |
|
11 |
|
12 def string(e: Exp) : String = e match { |
|
13 case N(n) => s"$n" |
|
14 case Plus(e1, e2) => s"(${string(e1)} + ${string(e2)})" |
|
15 case Times(e1, e2) => s"(${string(e1)} * ${string(e2)})" |
|
16 } |
|
17 |
|
18 val e = Plus(N(9), Times(N(3), N(4))) |
|
19 println(string(e)) |
|
20 |
|
21 def eval(e: Exp) : Int = e match { |
|
22 case N(n) => n |
|
23 case Plus(e1, e2) => eval(e1) + eval(e2) |
|
24 case Times(e1, e2) => eval(e1) * eval(e2) |
|
25 } |
|
26 |
|
27 println(eval(e)) |
|
28 |
|
29 // simplification rules: |
|
30 // e + 0, 0 + e => e |
|
31 // e * 0, 0 * e => 0 |
|
32 // e * 1, 1 * e => e |
|
33 |
|
34 def simp(e: Exp) : Exp = e match { |
|
35 case N(n) => N(n) |
|
36 case Plus(e1, e2) => (simp(e1), simp(e2)) match { |
|
37 case (N(0), e2s) => e2s |
|
38 case (e1s, N(0)) => e1s |
|
39 case (e1s, e2s) => Plus(e1s, e2s) |
|
40 } |
|
41 case Times(e1, e2) => (simp(e1), simp(e2)) match { |
|
42 case (N(0), _) => N(0) |
|
43 case (_, N(0)) => N(0) |
|
44 case (N(1), e2s) => e2s |
|
45 case (e1s, N(1)) => e1s |
|
46 case (e1s, e2s) => Times(e1s, e2s) |
|
47 } |
|
48 } |
|
49 |
|
50 |
|
51 val e2 = Times(Plus(N(0), N(1)), Plus(N(0), N(9))) |
|
52 println(string(e2)) |
|
53 println(string(simp(e2))) |
|
54 |
|
55 |
|
56 // Tokens and Reverse Polish Notation |
|
57 abstract class Token |
|
58 case class T(n: Int) extends Token |
|
59 case object PL extends Token |
|
60 case object TI extends Token |
|
61 |
|
62 // transfroming an Exp into a list of tokens |
|
63 def rp(e: Exp) : List[Token] = e match { |
|
64 case N(n) => List(T(n)) |
|
65 case Plus(e1, e2) => rp(e1) ::: rp(e2) ::: List(PL) |
|
66 case Times(e1, e2) => rp(e1) ::: rp(e2) ::: List(TI) |
|
67 } |
|
68 println(string(e2)) |
|
69 println(rp(e2)) |
|
70 |
|
71 def comp(ls: List[Token], st: List[Int]) : Int = (ls, st) match { |
|
72 case (Nil, st) => st.head |
|
73 case (T(n)::rest, st) => comp(rest, n::st) |
|
74 case (PL::rest, n1::n2::st) => comp(rest, n1 + n2::st) |
|
75 case (TI::rest, n1::n2::st) => comp(rest, n1 * n2::st) |
|
76 } |
|
77 |
|
78 comp(rp(e), Nil) |
|
79 |
|
80 def proc(s: String) : Token = s match { |
|
81 case "+" => PL |
|
82 case "*" => TI |
|
83 case _ => T(s.toInt) |
|
84 } |
|
85 |
|
86 comp("1 2 + 4 * 5 + 3 +".split(" ").toList.map(proc), Nil) |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 // Sudoku |
|
92 //======== |
|
93 |
|
94 // THE POINT OF THIS CODE IS NOT TO BE SUPER |
|
95 // EFFICIENT AND FAST, just explaining exhaustive |
|
96 // depth-first search |
|
97 |
|
98 |
|
99 val game0 = """.14.6.3.. |
|
100 |62...4..9 |
|
101 |.8..5.6.. |
|
102 |.6.2....3 |
|
103 |.7..1..5. |
|
104 |5....9.6. |
|
105 |..6.2..3. |
|
106 |1..5...92 |
|
107 |..7.9.41.""".stripMargin.replaceAll("\\n", "") |
|
108 |
|
109 type Pos = (Int, Int) |
|
110 val EmptyValue = '.' |
|
111 val MaxValue = 9 |
|
112 |
|
113 val allValues = "123456789".toList |
|
114 val indexes = (0 to 8).toList |
|
115 |
|
116 |
|
117 def empty(game: String) = game.indexOf(EmptyValue) |
|
118 def isDone(game: String) = empty(game) == -1 |
|
119 def emptyPosition(game: String) = |
|
120 (empty(game) % MaxValue, empty(game) / MaxValue) |
|
121 |
|
122 |
|
123 def get_row(game: String, y: Int) = |
|
124 indexes.map(col => game(y * MaxValue + col)) |
|
125 def get_col(game: String, x: Int) = |
|
126 indexes.map(row => game(x + row * MaxValue)) |
|
127 |
|
128 def get_box(game: String, pos: Pos): List[Char] = { |
|
129 def base(p: Int): Int = (p / 3) * 3 |
|
130 val x0 = base(pos._1) |
|
131 val y0 = base(pos._2) |
|
132 val ys = (y0 until y0 + 3).toList |
|
133 (x0 until x0 + 3).toList.flatMap(x => ys.map(y => game(x + y * MaxValue))) |
|
134 } |
|
135 |
|
136 //get_row(game0, 0) |
|
137 //get_row(game0, 1) |
|
138 //get_col(game0, 0) |
|
139 //get_box(game0, (3, 1)) |
|
140 |
|
141 |
|
142 // this is not mutable!! |
|
143 def update(game: String, pos: Int, value: Char): String = |
|
144 game.updated(pos, value) |
|
145 |
|
146 def toAvoid(game: String, pos: Pos): List[Char] = |
|
147 (get_col(game, pos._1) ++ get_row(game, pos._2) ++ get_box(game, pos)) |
|
148 |
|
149 def candidates(game: String, pos: Pos): List[Char] = |
|
150 allValues.diff(toAvoid(game, pos)) |
|
151 |
|
152 //candidates(game0, (0,0)) |
|
153 |
|
154 def pretty(game: String): String = |
|
155 "\n" + (game.sliding(MaxValue, MaxValue).mkString("\n")) |
|
156 |
|
157 |
|
158 def search(game: String): List[String] = { |
|
159 if (isDone(game)) List(game) |
|
160 else { |
|
161 val cs = candidates(game, emptyPosition(game)) |
|
162 cs.map(c => search(update(game, empty(game), c))).toList.flatten |
|
163 } |
|
164 } |
|
165 |
|
166 search(game0).map(pretty) |
|
167 |
|
168 val game1 = """23.915... |
|
169 |...2..54. |
|
170 |6.7...... |
|
171 |..1.....9 |
|
172 |89.5.3.17 |
|
173 |5.....6.. |
|
174 |......9.5 |
|
175 |.16..7... |
|
176 |...329..1""".stripMargin.replaceAll("\\n", "") |
|
177 |
|
178 search(game1).map(pretty) |
|
179 |
|
180 // a game that is in the hard category |
|
181 val game2 = """8........ |
|
182 |..36..... |
|
183 |.7..9.2.. |
|
184 |.5...7... |
|
185 |....457.. |
|
186 |...1...3. |
|
187 |..1....68 |
|
188 |..85...1. |
|
189 |.9....4..""".stripMargin.replaceAll("\\n", "") |
|
190 |
|
191 search(game2).map(pretty) |
|
192 |
|
193 // game with multiple solutions |
|
194 val game3 = """.8...9743 |
|
195 |.5...8.1. |
|
196 |.1....... |
|
197 |8....5... |
|
198 |...8.4... |
|
199 |...3....6 |
|
200 |.......7. |
|
201 |.3.5...8. |
|
202 |9724...5.""".stripMargin.replaceAll("\\n", "") |
|
203 |
|
204 search(game3).map(pretty).foreach(println) |
|
205 |
|
206 // for measuring time |
|
207 def time_needed[T](i: Int, code: => T) = { |
|
208 val start = System.nanoTime() |
|
209 for (j <- 1 to i) code |
|
210 val end = System.nanoTime() |
|
211 s"${(end - start) / 1.0e9} secs" |
|
212 } |
|
213 |
|
214 time_needed(1, search(game2)) |
|
215 |
|
216 |
|
217 |
|
218 // Tail recursion |
|
219 //================ |
|
220 |
|
221 |
|
222 def fact(n: Long): Long = |
|
223 if (n == 0) 1 else n * fact(n - 1) |
|
224 |
|
225 |
|
226 fact(10) // ok |
|
227 fact(1000) // silly |
|
228 fact(10000) // produces a stackoverflow |
|
229 |
|
230 def factB(n: BigInt): BigInt = |
|
231 if (n == 0) 1 else n * factB(n - 1) |
|
232 |
|
233 factB(1000) |
|
234 |
|
235 |
|
236 def factT(n: BigInt, acc: BigInt): BigInt = |
|
237 if (n == 0) acc else factT(n - 1, n * acc) |
|
238 |
|
239 factT(10, 1) |
|
240 println(factT(100000, 1)) |
|
241 |
|
242 // there is a flag for ensuring a function is tail recursive |
|
243 import scala.annotation.tailrec |
|
244 |
|
245 @tailrec |
|
246 def factT(n: BigInt, acc: BigInt): BigInt = |
|
247 if (n == 0) acc else factT(n - 1, n * acc) |
|
248 |
|
249 factT(100000, 1) |
|
250 |
|
251 // for tail-recursive functions the Scala compiler |
|
252 // generates loop-like code, which does not need |
|
253 // to allocate stack-space in each recursive |
|
254 // call; Scala can do this only for tail-recursive |
|
255 // functions |
|
256 |
|
257 // tail recursive version that searches |
|
258 // for all Sudoku solutions |
|
259 |
|
260 |
|
261 def searchT(games: List[String], sols: List[String]): List[String] = games match { |
|
262 case Nil => sols |
|
263 case game::rest => { |
|
264 if (isDone(game)) searchT(rest, game::sols) |
|
265 else { |
|
266 val cs = candidates(game, emptyPosition(game)) |
|
267 searchT(cs.map(c => update(game, empty(game), c)) ::: rest, sols) |
|
268 } |
|
269 } |
|
270 } |
|
271 |
|
272 searchT(List(game3), List()).map(pretty) |
|
273 |
|
274 |
|
275 // tail recursive version that searches |
|
276 // for a single solution |
|
277 |
|
278 def search1T(games: List[String]): Option[String] = games match { |
|
279 case Nil => None |
|
280 case game::rest => { |
|
281 if (isDone(game)) Some(game) |
|
282 else { |
|
283 val cs = candidates(game, emptyPosition(game)) |
|
284 search1T(cs.map(c => update(game, empty(game), c)) ::: rest) |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 search1T(List(game3)).map(pretty) |
|
290 time_needed(1, search1T(List(game3))) |
|
291 time_needed(1, search1T(List(game2))) |
|
292 |
|
293 // game with multiple solutions |
|
294 val game3 = """.8...9743 |
|
295 |.5...8.1. |
|
296 |.1....... |
|
297 |8....5... |
|
298 |...8.4... |
|
299 |...3....6 |
|
300 |.......7. |
|
301 |.3.5...8. |
|
302 |9724...5.""".stripMargin.replaceAll("\\n", "") |
|
303 |
|
304 searchT(List(game3), Nil).map(pretty) |
|
305 search1T(List(game3)).map(pretty) |
|
306 |
|
307 // Moral: Whenever a recursive function is resource-critical |
|
308 // (i.e. works with large recursion depth), then you need to |
|
309 // write it in tail-recursive fashion. |
|
310 // |
|
311 // Unfortuantely, Scala because of current limitations in |
|
312 // the JVM is not as clever as other functional languages. It can |
|
313 // only optimise "self-tail calls". This excludes the cases of |
|
314 // multiple functions making tail calls to each other. Well, |
|
315 // nothing is perfect. |
|
316 |
|
317 |
3 |
318 |
4 |
319 |
5 // Polymorphic Types |
320 // Polymorphic Types |
6 //=================== |
321 //=================== |
7 |
322 |
102 |
414 |
103 var arr = Array[Int]() |
415 var arr = Array[Int]() |
104 arr(0) = "Hello World" |
416 arr(0) = "Hello World" |
105 |
417 |
106 |
418 |
|
419 |
|
420 |
|
421 // Cool Stuff in Scala |
|
422 //===================== |
|
423 |
|
424 |
|
425 // Implicits or How to Pimp your Library |
|
426 //====================================== |
|
427 // |
|
428 // For example adding your own methods to Strings: |
|
429 // Imagine you want to increment strings, like |
|
430 // |
|
431 // "HAL".increment |
|
432 // |
|
433 // you can avoid ugly fudges, like a MyString, by |
|
434 // using implicit conversions. |
|
435 |
|
436 |
|
437 implicit class MyString(s: String) { |
|
438 def increment = s.map(c => (c + 1).toChar) |
|
439 } |
|
440 |
|
441 "HAL".increment |
|
442 |
|
443 |
|
444 // Abstract idea: |
|
445 // In that version implicit conversions were used to solve the |
|
446 // late extension problem; namely, given a class C and a class T, |
|
447 // how to have C extend T without touching or recompiling C. |
|
448 // Conversions add a wrapper when a member of T is requested |
|
449 // from an instance of C. |
|
450 |
|
451 //Another example (TimeUnit in 2.13?) |
|
452 |
|
453 import scala.concurrent.duration.{TimeUnit,SECONDS,MINUTES} |
|
454 |
|
455 case class Duration(time: Long, unit: TimeUnit) { |
|
456 def +(o: Duration) = |
|
457 Duration(time + unit.convert(o.time, o.unit), unit) |
|
458 } |
|
459 |
|
460 implicit class Int2Duration(that: Int) { |
|
461 def seconds = new Duration(that, SECONDS) |
|
462 def minutes = new Duration(that, MINUTES) |
|
463 } |
|
464 |
|
465 5.seconds + 2.minutes //Duration(125L, SECONDS ) |
|
466 2.minutes + 60.seconds |
|
467 |
|
468 |
|
469 |
|
470 |
|
471 // Regular expressions - the power of DSLs in Scala |
|
472 //================================================== |
|
473 |
|
474 abstract class Rexp |
|
475 case object ZERO extends Rexp // nothing |
|
476 case object ONE extends Rexp // the empty string |
|
477 case class CHAR(c: Char) extends Rexp // a character c |
|
478 case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2 |
|
479 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 . r2 |
|
480 case class STAR(r: Rexp) extends Rexp // star r* |
|
481 |
|
482 |
|
483 |
|
484 // writing (ab)* in the format above is |
|
485 // tedious |
|
486 val r0 = STAR(SEQ(CHAR('a'), CHAR('b'))) |
|
487 |
|
488 |
|
489 // some convenience for typing in regular expressions |
|
490 import scala.language.implicitConversions |
|
491 import scala.language.reflectiveCalls |
|
492 |
|
493 def charlist2rexp(s: List[Char]): Rexp = s match { |
|
494 case Nil => ONE |
|
495 case c::Nil => CHAR(c) |
|
496 case c::s => SEQ(CHAR(c), charlist2rexp(s)) |
|
497 } |
|
498 implicit def string2rexp(s: String): Rexp = |
|
499 charlist2rexp(s.toList) |
|
500 |
|
501 |
|
502 val r1 = STAR("ab") |
|
503 val r2 = STAR(ALT("ab", "baa baa black sheep")) |
|
504 val r3 = STAR(SEQ("ab", ALT("a", "b"))) |
|
505 |
|
506 implicit def RexpOps (r: Rexp) = new { |
|
507 def | (s: Rexp) = ALT(r, s) |
|
508 def % = STAR(r) |
|
509 def ~ (s: Rexp) = SEQ(r, s) |
|
510 } |
|
511 |
|
512 implicit def stringOps (s: String) = new { |
|
513 def | (r: Rexp) = ALT(s, r) |
|
514 def | (r: String) = ALT(s, r) |
|
515 def % = STAR(s) |
|
516 def ~ (r: Rexp) = SEQ(s, r) |
|
517 def ~ (r: String) = SEQ(s, r) |
|
518 } |
|
519 |
|
520 //example regular expressions |
|
521 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
|
522 val sign = "+" | "-" | "" |
|
523 val number = sign ~ digit ~ digit.% |
107 |
524 |
108 |
525 |
109 // |
526 // |
110 // Object Oriented Programming in Scala |
527 // Object Oriented Programming in Scala |
111 // |
528 // |
401 |
818 |
402 |
819 |
403 |
820 |
404 |
821 |
405 |
822 |
406 // Cool Stuff in Scala |
|
407 //===================== |
|
408 |
|
409 |
|
410 // Implicits or How to Pimp my Library |
|
411 //===================================== |
|
412 // |
|
413 // For example adding your own methods to Strings: |
|
414 // Imagine you want to increment strings, like |
|
415 // |
|
416 // "HAL".increment |
|
417 // |
|
418 // you can avoid ugly fudges, like a MyString, by |
|
419 // using implicit conversions. |
|
420 |
|
421 |
|
422 implicit class MyString(s: String) { |
|
423 def increment = for (c <- s) yield (c + 1).toChar |
|
424 } |
|
425 |
|
426 "HAL".increment |
|
427 |
|
428 |
|
429 // Abstract idea: |
|
430 // In that version implicit conversions were used to solve the |
|
431 // late extension problem; namely, given a class C and a class T, |
|
432 // how to have C extend T without touching or recompiling C. |
|
433 // Conversions add a wrapper when a member of T is requested |
|
434 // from an instance of C. |
|
435 |
|
436 //Another example (TimeUnit in 2.13?) |
|
437 |
|
438 case class Duration(time: Long, unit: TimeUnit) { |
|
439 def +(o: Duration) = Duration(time + unit.convert(o.time, o.unit), unit) |
|
440 } |
|
441 |
|
442 implicit class Int2Duration(that: Int) { |
|
443 def seconds = new Duration(that, SECONDS) |
|
444 def minutes = new Duration(that, MINUTES) |
|
445 } |
|
446 |
|
447 5.seconds + 2.minutes //Duration(125L, SECONDS ) |
|
448 |
|
449 |
|
450 |
|
451 // Regular expressions - the power of DSLs in Scala |
|
452 //================================================== |
|
453 |
|
454 abstract class Rexp |
|
455 case object ZERO extends Rexp // nothing |
|
456 case object ONE extends Rexp // the empty string |
|
457 case class CHAR(c: Char) extends Rexp // a character c |
|
458 case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2 |
|
459 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 . r2 |
|
460 case class STAR(r: Rexp) extends Rexp // star r* |
|
461 |
|
462 |
|
463 |
|
464 // writing (ab)* in the format above is |
|
465 // tedious |
|
466 val r0 = STAR(SEQ(CHAR('a'), CHAR('b'))) |
|
467 |
|
468 |
|
469 // some convenience for typing in regular expressions |
|
470 import scala.language.implicitConversions |
|
471 import scala.language.reflectiveCalls |
|
472 |
|
473 def charlist2rexp(s: List[Char]): Rexp = s match { |
|
474 case Nil => ONE |
|
475 case c::Nil => CHAR(c) |
|
476 case c::s => SEQ(CHAR(c), charlist2rexp(s)) |
|
477 } |
|
478 implicit def string2rexp(s: String): Rexp = |
|
479 charlist2rexp(s.toList) |
|
480 |
|
481 |
|
482 val r1 = STAR("ab") |
|
483 val r2 = STAR(ALT("ab", "baa baa black sheep")) |
|
484 val r3 = STAR(SEQ("ab", ALT("a", "b"))) |
|
485 |
|
486 implicit def RexpOps (r: Rexp) = new { |
|
487 def | (s: Rexp) = ALT(r, s) |
|
488 def % = STAR(r) |
|
489 def ~ (s: Rexp) = SEQ(r, s) |
|
490 } |
|
491 |
|
492 |
|
493 implicit def stringOps (s: String) = new { |
|
494 def | (r: Rexp) = ALT(s, r) |
|
495 def | (r: String) = ALT(s, r) |
|
496 def % = STAR(s) |
|
497 def ~ (r: Rexp) = SEQ(s, r) |
|
498 def ~ (r: String) = SEQ(s, r) |
|
499 } |
|
500 |
|
501 //example regular expressions |
|
502 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
|
503 val sign = "+" | "-" | "" |
|
504 val number = sign ~ digit ~ digit.% |
|
505 |
|
506 |
|
507 |
823 |
508 // Lazy Evaluation |
824 // Lazy Evaluation |
509 //================= |
825 //================= |
510 // |
826 // |
511 // Do not evaluate arguments just yet: |
827 // Do not evaluate arguments just yet: |