1 // Scala Lecture 5 |
1 // Scala Lecture 5 |
2 //================= |
2 //================= |
3 |
3 |
4 // Questions at |
4 // (Immutable) |
5 // |
5 // Object Oriented Programming in Scala |
6 // pollev.com/cfltutoratki576 |
6 // ===================================== |
7 |
7 |
8 (n, m) match { |
8 |
9 case Pat1 => |
9 abstract class Animal |
10 case _ => |
10 case class Bird(name: String) extends Animal { |
11 } |
11 override def toString = name |
|
12 } |
|
13 case class Mammal(name: String) extends Animal |
|
14 case class Reptile(name: String) extends Animal |
|
15 |
|
16 Mammal("Zebra") |
|
17 println(Mammal("Zebra")) |
|
18 println(Mammal("Zebra").toString) |
|
19 |
|
20 |
|
21 Bird("Sparrow") |
|
22 println(Bird("Sparrow")) |
|
23 println(Bird("Sparrow").toString) |
|
24 |
|
25 Bird("Sparrow").copy(name = "House Sparrow") |
|
26 |
|
27 def group(a : Animal) = a match { |
|
28 case Bird(_) => "It's a bird" |
|
29 case Mammal(_) => "It's a mammal" |
|
30 } |
|
31 |
|
32 |
|
33 // There is a very convenient short-hand notation |
|
34 // for constructors: |
|
35 |
|
36 class Fraction(x: Int, y: Int) { |
|
37 def numer = x |
|
38 def denom = y |
|
39 } |
|
40 |
|
41 val half = new Fraction(1, 2) |
|
42 half.numer |
|
43 |
|
44 case class Fraction(numer: Int, denom: Int) |
|
45 |
|
46 val half = Fraction(1, 2) |
|
47 |
|
48 half.numer |
|
49 half.denom |
|
50 |
|
51 |
|
52 // In mandelbrot.scala I used complex (imaginary) numbers |
|
53 // and implemented the usual arithmetic operations for complex |
|
54 // numbers. |
|
55 |
|
56 case class Complex(re: Double, im: Double) { |
|
57 // represents the complex number re + im * i |
|
58 def foo(that: Complex) = Complex(this.re + that.re, this.im + that.im) |
|
59 def -(that: Complex) = Complex(this.re - that.re, this.im - that.im) |
|
60 def *(that: Complex) = Complex(this.re * that.re - this.im * that.im, |
|
61 this.re * that.im + that.re * this.im) |
|
62 def *(that: Double) = Complex(this.re * that, this.im * that) |
|
63 def abs = Math.sqrt(this.re * this.re + this.im * this.im) |
|
64 } |
|
65 |
|
66 object.method(....) |
|
67 |
|
68 val test = Complex(1, 2) + Complex (3, 4) |
|
69 |
|
70 import scala.language.postfixOps |
|
71 (List(5,4,3,2,1) sorted) reverse |
|
72 |
|
73 // this could have equally been written as |
|
74 val test = Complex(1, 2).+(Complex (3, 4)) |
|
75 |
|
76 // this applies to all methods, but requires |
|
77 import scala.language.postfixOps |
|
78 |
|
79 List(5, 2, 3, 4).sorted |
|
80 List(5, 2, 3, 4) sorted |
|
81 |
|
82 |
|
83 // ...to allow the notation n + m * i |
|
84 import scala.language.implicitConversions |
|
85 |
|
86 val i = Complex(0, 1) |
|
87 implicit def double2complex(re: Double) = Complex(re, 0) |
|
88 |
|
89 |
|
90 val inum1 = -2.0 + -1.5 * i |
|
91 val inum2 = 1.0 + 1.5 * i |
|
92 |
|
93 |
|
94 |
|
95 // All is public by default....so no public is needed. |
|
96 // You can have the usual restrictions about private |
|
97 // values and methods, if you are MUTABLE !!! |
|
98 |
|
99 case class BankAccount(init: Int) { |
|
100 |
|
101 private var balance = init |
|
102 |
|
103 def deposit(amount: Int): Unit = { |
|
104 if (amount > 0) balance = balance + amount |
|
105 } |
|
106 |
|
107 def withdraw(amount: Int): Int = |
|
108 if (0 < amount && amount <= balance) { |
|
109 balance = balance - amount |
|
110 balance |
|
111 } else throw new Error("insufficient funds") |
|
112 } |
|
113 |
|
114 // BUT since we are completely IMMUTABLE, this is |
|
115 // virtually of no concern to us. |
|
116 |
|
117 |
|
118 |
|
119 // another example about Fractions |
|
120 import scala.language.implicitConversions |
|
121 import scala.language.reflectiveCalls |
|
122 |
|
123 case class Fraction(numer: Int, denom: Int) { |
|
124 override def toString = numer.toString + "/" + denom.toString |
|
125 |
|
126 def +(other: Fraction) = |
|
127 Fraction(numer * other.denom + other.numer * denom, |
|
128 denom * other.denom) |
|
129 def *(other: Fraction) = Fraction(numer * other.numer, denom * other.denom) |
|
130 } |
|
131 |
|
132 implicit def Int2Fraction(x: Int) = Fraction(x, 1) |
|
133 |
|
134 val half = Fraction(1, 2) |
|
135 val third = Fraction (1, 3) |
|
136 |
|
137 half + third |
|
138 half * third |
|
139 |
|
140 1 + half |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 // DFAs in Scala |
|
146 //=============== |
|
147 import scala.util.Try |
|
148 |
|
149 |
|
150 // A is the state type |
|
151 // C is the input (usually characters) |
|
152 |
|
153 case class DFA[A, C](start: A, // starting state |
|
154 delta: (A, C) => A, // transition function |
|
155 fins: A => Boolean) { // final states (Set) |
|
156 |
|
157 def deltas(q: A, s: List[C]) : A = s match { |
|
158 case Nil => q |
|
159 case c::cs => deltas(delta(q, c), cs) |
|
160 } |
|
161 |
|
162 def accepts(s: List[C]) : Boolean = |
|
163 Try(fins(deltas(start, s))).getOrElse(false) |
|
164 } |
|
165 |
|
166 // the example shown in the handout |
|
167 abstract class State |
|
168 case object Q0 extends State |
|
169 case object Q1 extends State |
|
170 case object Q2 extends State |
|
171 case object Q3 extends State |
|
172 case object Q4 extends State |
12 |
173 |
13 val delta : (State, Char) => State = |
174 val delta : (State, Char) => State = |
14 { case (Q0, 'a') => Q1 |
175 { case (Q0, 'a') => Q1 |
15 case (Q0, 'b') => Q2 |
176 case (Q0, 'b') => Q2 |
16 case (Q1, 'a') => Q4 |
177 case (Q1, 'a') => Q4 |
21 case (Q3, 'b') => Q0 |
182 case (Q3, 'b') => Q0 |
22 case (Q4, 'a') => Q4 |
183 case (Q4, 'a') => Q4 |
23 case (Q4, 'b') => Q4 |
184 case (Q4, 'b') => Q4 |
24 case _ => throw new Exception("Undefined") } |
185 case _ => throw new Exception("Undefined") } |
25 |
186 |
26 |
187 val dfa = DFA(Q0, delta, Set[State](Q4)) |
27 class Foo(i: Int) |
188 |
28 |
189 dfa.accepts("abaaa".toList) // true |
29 val v = new Foo(10) |
190 dfa.accepts("bbabaab".toList) // true |
30 |
191 dfa.accepts("baba".toList) // false |
31 case class Bar(i: Int) |
192 dfa.accepts("abc".toList) // false |
32 |
193 |
33 val v = Bar(10) |
194 |
34 |
195 // NFAs (Nondeterministic Finite Automata) |
35 |
196 |
36 |
197 |
37 |
198 case class NFA[A, C](starts: Set[A], // starting states |
38 |
199 delta: (A, C) => Set[A], // transition function |
39 |
200 fins: A => Boolean) { // final states |
40 |
201 |
41 |
202 // given a state and a character, what is the set of |
42 |
203 // next states? if there is none => empty set |
43 |
204 def next(q: A, c: C) : Set[A] = |
|
205 Try(delta(q, c)).getOrElse(Set[A]()) |
|
206 |
|
207 def nexts(qs: Set[A], c: C) : Set[A] = |
|
208 qs.flatMap(next(_, c)) |
|
209 |
|
210 // depth-first version of accepts |
|
211 def search(q: A, s: List[C]) : Boolean = s match { |
|
212 case Nil => fins(q) |
|
213 case c::cs => next(q, c).exists(search(_, cs)) |
|
214 } |
|
215 |
|
216 def accepts(s: List[C]) : Boolean = |
|
217 starts.exists(search(_, s)) |
|
218 } |
|
219 |
|
220 |
|
221 |
|
222 // NFA examples |
|
223 |
|
224 val nfa_trans1 : (State, Char) => Set[State] = |
|
225 { case (Q0, 'a') => Set(Q0, Q1) |
|
226 case (Q0, 'b') => Set(Q2) |
|
227 case (Q1, 'a') => Set(Q1) |
|
228 case (Q2, 'b') => Set(Q2) } |
|
229 |
|
230 val nfa = NFA(Set[State](Q0), nfa_trans1, Set[State](Q2)) |
|
231 |
|
232 nfa.accepts("aa".toList) // false |
|
233 nfa.accepts("aaaaa".toList) // false |
|
234 nfa.accepts("aaaaab".toList) // true |
|
235 nfa.accepts("aaaaabbb".toList) // true |
|
236 nfa.accepts("aaaaabbbaaa".toList) // false |
|
237 nfa.accepts("ac".toList) // false |
|
238 |
|
239 |
|
240 // Q: Why the kerfuffle about the polymorphic types in DFAs/NFAs? |
|
241 // A: Subset construction. Here the state type for the DFA is |
|
242 // sets of states. |
|
243 |
|
244 |
|
245 def subset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = { |
|
246 DFA(nfa.starts, |
|
247 { case (qs, c) => nfa.nexts(qs, c) }, |
|
248 _.exists(nfa.fins)) |
|
249 } |
|
250 |
|
251 subset(nfa).accepts("aa".toList) // false |
|
252 subset(nfa).accepts("aaaaa".toList) // false |
|
253 subset(nfa).accepts("aaaaab".toList) // true |
|
254 subset(nfa).accepts("aaaaabbb".toList) // true |
|
255 subset(nfa).accepts("aaaaabbbaaa".toList) // false |
|
256 subset(nfa).accepts("ac".toList) // false |
|
257 |
|
258 import scala.math.pow |
44 |
259 |
45 |
260 |
46 // Laziness with style |
261 // Laziness with style |
47 //===================== |
262 //===================== |
48 |
263 |
208 .dropWhile(depth(_) < 3) |
423 .dropWhile(depth(_) < 3) |
209 .take(10).foreach(println)) |
424 .take(10).foreach(println)) |
210 |
425 |
211 |
426 |
212 |
427 |
213 // (Immutable) |
|
214 // Object Oriented Programming in Scala |
|
215 // ===================================== |
|
216 |
|
217 |
|
218 abstract class Animal |
|
219 case class Bird(name: String) extends Animal { |
|
220 override def toString = name |
|
221 } |
|
222 case class Mammal(name: String) extends Animal |
|
223 case class Reptile(name: String) extends Animal |
|
224 |
|
225 Mammal("Zebra") |
|
226 println(Mammal("Zebra")) |
|
227 println(Mammal("Zebra").toString) |
|
228 |
|
229 |
|
230 Bird("Sparrow") |
|
231 println(Bird("Sparrow")) |
|
232 println(Bird("Sparrow").toString) |
|
233 |
|
234 Bird("Sparrow").copy(name = "House Sparrow") |
|
235 |
|
236 def group(a : Animal) = a match { |
|
237 case Bird(_) => "It's a bird" |
|
238 case Mammal(_) => "It's a mammal" |
|
239 } |
|
240 |
|
241 |
|
242 // There is a very convenient short-hand notation |
|
243 // for constructors: |
|
244 |
|
245 class Fraction(x: Int, y: Int) { |
|
246 def numer = x |
|
247 def denom = y |
|
248 } |
|
249 |
|
250 val half = new Fraction(1, 2) |
|
251 half.numer |
|
252 |
|
253 case class Fraction(numer: Int, denom: Int) |
|
254 |
|
255 val half = Fraction(1, 2) |
|
256 |
|
257 half.numer |
|
258 half.denom |
|
259 |
|
260 |
|
261 // In mandelbrot.scala I used complex (imaginary) numbers |
|
262 // and implemented the usual arithmetic operations for complex |
|
263 // numbers. |
|
264 |
|
265 case class Complex(re: Double, im: Double) { |
|
266 // represents the complex number re + im * i |
|
267 def +(that: Complex) = Complex(this.re + that.re, this.im + that.im) |
|
268 def -(that: Complex) = Complex(this.re - that.re, this.im - that.im) |
|
269 def *(that: Complex) = Complex(this.re * that.re - this.im * that.im, |
|
270 this.re * that.im + that.re * this.im) |
|
271 def *(that: Double) = Complex(this.re * that, this.im * that) |
|
272 def abs = Math.sqrt(this.re * this.re + this.im * this.im) |
|
273 } |
|
274 |
|
275 val test = Complex(1, 2) + Complex (3, 4) |
|
276 |
|
277 import scala.language.postfixOps |
|
278 List(5,4,3,2,1).sorted.reverse |
|
279 |
|
280 // this could have equally been written as |
|
281 val test = Complex(1, 2).+(Complex (3, 4)) |
|
282 |
|
283 // this applies to all methods, but requires |
|
284 import scala.language.postfixOps |
|
285 |
|
286 List(5, 2, 3, 4).sorted |
|
287 List(5, 2, 3, 4) sorted |
|
288 |
|
289 |
|
290 // ...to allow the notation n + m * i |
|
291 import scala.language.implicitConversions |
|
292 |
|
293 val i = Complex(0, 1) |
|
294 implicit def double2complex(re: Double) = Complex(re, 0) |
|
295 |
|
296 |
|
297 val inum1 = -2.0 + -1.5 * i |
|
298 val inum2 = 1.0 + 1.5 * i |
|
299 |
|
300 |
|
301 |
|
302 // All is public by default....so no public is needed. |
|
303 // You can have the usual restrictions about private |
|
304 // values and methods, if you are MUTABLE !!! |
|
305 |
|
306 case class BankAccount(init: Int) { |
|
307 |
|
308 private var balance = init |
|
309 |
|
310 def deposit(amount: Int): Unit = { |
|
311 if (amount > 0) balance = balance + amount |
|
312 } |
|
313 |
|
314 def withdraw(amount: Int): Int = |
|
315 if (0 < amount && amount <= balance) { |
|
316 balance = balance - amount |
|
317 balance |
|
318 } else throw new Error("insufficient funds") |
|
319 } |
|
320 |
|
321 // BUT since we are completely IMMUTABLE, this is |
|
322 // virtually of no concern to us. |
|
323 |
|
324 |
|
325 |
|
326 // another example about Fractions |
|
327 import scala.language.implicitConversions |
|
328 import scala.language.reflectiveCalls |
|
329 |
|
330 case class Fraction(numer: Int, denom: Int) { |
|
331 override def toString = numer.toString + "/" + denom.toString |
|
332 |
|
333 def +(other: Fraction) = |
|
334 Fraction(numer * other.denom + other.numer * denom, |
|
335 denom * other.denom) |
|
336 def *(other: Fraction) = Fraction(numer * other.numer, denom * other.denom) |
|
337 } |
|
338 |
|
339 implicit def Int2Fraction(x: Int) = Fraction(x, 1) |
|
340 |
|
341 val half = Fraction(1, 2) |
|
342 val third = Fraction (1, 3) |
|
343 |
|
344 half + third |
|
345 half * third |
|
346 |
|
347 1 + half |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 // DFAs in Scala |
|
353 //=============== |
|
354 import scala.util.Try |
|
355 |
|
356 |
|
357 // A is the state type |
|
358 // C is the input (usually characters) |
|
359 |
|
360 case class DFA[A, C](start: A, // starting state |
|
361 delta: (A, C) => A, // transition function |
|
362 fins: A => Boolean) { // final states (Set) |
|
363 |
|
364 def deltas(q: A, s: List[C]) : A = s match { |
|
365 case Nil => q |
|
366 case c::cs => deltas(delta(q, c), cs) |
|
367 } |
|
368 |
|
369 def accepts(s: List[C]) : Boolean = |
|
370 Try(fins(deltas(start, s))).getOrElse(false) |
|
371 } |
|
372 |
|
373 // the example shown in the handout |
|
374 abstract class State |
|
375 case object Q0 extends State |
|
376 case object Q1 extends State |
|
377 case object Q2 extends State |
|
378 case object Q3 extends State |
|
379 case object Q4 extends State |
|
380 |
|
381 val delta : (State, Char) => State = |
|
382 { case (Q0, 'a') => Q1 |
|
383 case (Q0, 'b') => Q2 |
|
384 case (Q1, 'a') => Q4 |
|
385 case (Q1, 'b') => Q2 |
|
386 case (Q2, 'a') => Q3 |
|
387 case (Q2, 'b') => Q2 |
|
388 case (Q3, 'a') => Q4 |
|
389 case (Q3, 'b') => Q0 |
|
390 case (Q4, 'a') => Q4 |
|
391 case (Q4, 'b') => Q4 |
|
392 case _ => throw new Exception("Undefined") } |
|
393 |
|
394 val dfa = DFA(Q0, delta, Set[State](Q4)) |
|
395 |
|
396 dfa.accepts("abaaa".toList) // true |
|
397 dfa.accepts("bbabaab".toList) // true |
|
398 dfa.accepts("baba".toList) // false |
|
399 dfa.accepts("abc".toList) // false |
|
400 |
|
401 |
|
402 // NFAs (Nondeterministic Finite Automata) |
|
403 |
|
404 |
|
405 case class NFA[A, C](starts: Set[A], // starting states |
|
406 delta: (A, C) => Set[A], // transition function |
|
407 fins: A => Boolean) { // final states |
|
408 |
|
409 // given a state and a character, what is the set of |
|
410 // next states? if there is none => empty set |
|
411 def next(q: A, c: C) : Set[A] = |
|
412 Try(delta(q, c)).getOrElse(Set[A]()) |
|
413 |
|
414 def nexts(qs: Set[A], c: C) : Set[A] = |
|
415 qs.flatMap(next(_, c)) |
|
416 |
|
417 // depth-first version of accepts |
|
418 def search(q: A, s: List[C]) : Boolean = s match { |
|
419 case Nil => fins(q) |
|
420 case c::cs => next(q, c).exists(search(_, cs)) |
|
421 } |
|
422 |
|
423 def accepts(s: List[C]) : Boolean = |
|
424 starts.exists(search(_, s)) |
|
425 } |
|
426 |
|
427 |
|
428 |
|
429 // NFA examples |
|
430 |
|
431 val nfa_trans1 : (State, Char) => Set[State] = |
|
432 { case (Q0, 'a') => Set(Q0, Q1) |
|
433 case (Q0, 'b') => Set(Q2) |
|
434 case (Q1, 'a') => Set(Q1) |
|
435 case (Q2, 'b') => Set(Q2) } |
|
436 |
|
437 val nfa = NFA(Set[State](Q0), nfa_trans1, Set[State](Q2)) |
|
438 |
|
439 nfa.accepts("aa".toList) // false |
|
440 nfa.accepts("aaaaa".toList) // false |
|
441 nfa.accepts("aaaaab".toList) // true |
|
442 nfa.accepts("aaaaabbb".toList) // true |
|
443 nfa.accepts("aaaaabbbaaa".toList) // false |
|
444 nfa.accepts("ac".toList) // false |
|
445 |
|
446 |
|
447 // Q: Why the kerfuffle about the polymorphic types in DFAs/NFAs? |
|
448 // A: Subset construction. Here the state type for the DFA is |
|
449 // sets of states. |
|
450 |
|
451 |
|
452 def subset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = { |
|
453 DFA(nfa.starts, |
|
454 { case (qs, c) => nfa.nexts(qs, c) }, |
|
455 _.exists(nfa.fins)) |
|
456 } |
|
457 |
|
458 subset(nfa).accepts("aa".toList) // false |
|
459 subset(nfa).accepts("aaaaa".toList) // false |
|
460 subset(nfa).accepts("aaaaab".toList) // true |
|
461 subset(nfa).accepts("aaaaabbb".toList) // true |
|
462 subset(nfa).accepts("aaaaabbbaaa".toList) // false |
|
463 subset(nfa).accepts("ac".toList) // false |
|
464 |
|
465 import scala.math.pow |
|
466 |
|
467 |
|
468 |
|
469 |
428 |
470 |
429 |
471 |
430 |
472 |
431 |
473 |
432 |
523 List(1, 2, 3).contains("your cup") // should not compile, but retruns false |
482 List(1, 2, 3).contains("your cup") // should not compile, but retruns false |
524 |
483 |
525 List(1, 2, 3) == Vector(1, 2, 3) // again should not compile, but returns true |
484 List(1, 2, 3) == Vector(1, 2, 3) // again should not compile, but returns true |
526 |
485 |
527 |
486 |
528 // I like best about Scala that it lets me often write |
487 |
529 // concise, readable code. And it hooks up with the |
|
530 // Isabelle theorem prover. |
|
531 |
|
532 |
|
533 // Puzzlers |
|
534 |
|
535 val month = 12 |
|
536 val day = 24 |
|
537 val (hour, min, sec) = (12, 0, 0) |
|
538 |
|
539 // use lowercase names for variable |
|
540 |
|
541 |
|
542 //================== |
|
543 val oneTwo = Seq(1, 2, 3).permutations |
|
544 |
|
545 if (oneTwo.length > 0) { |
|
546 println("Permutations of 1,2 and 3:") |
|
547 oneTwo.foreach(println) |
|
548 } |
|
549 |
|
550 val threeFour = Seq(3, 4, 5).permutations |
|
551 |
|
552 if (!threeFour.isEmpty) { |
|
553 println("Permutations of 3, 4 and 5:") |
|
554 threeFour.foreach(println) |
|
555 } |
|
556 |
|
557 //================== |
|
558 val (a, b, c) = |
|
559 if (4 < 5) { |
|
560 "bar" |
|
561 } else { |
|
562 Some(10) |
|
563 } |
|
564 |
|
565 //Because when an expression has multiple return branches, Scala tries to |
|
566 //be helpful, by picking the first common ancestor type of all the |
|
567 //branches as the type of the whole expression. |
|
568 // |
|
569 //In this case, one branch has type String and the other has type |
|
570 //Option[Int], so the compiler decides that what the developer really |
|
571 //wants is for the whole if/else expression to have type Serializable, |
|
572 //since that’s the most specific type to claim both String and Option as |
|
573 //descendants. |
|
574 // |
|
575 //And guess what, Tuple3[A, B, C] is also Serializable, so as far as the |
|
576 //compiler is concerned, the assignment of the whole mess to (a, b, c) |
|
577 //can’t be proven invalid. So it gets through with a warning, |
|
578 //destined to fail at runtime. |
|
579 |
|
580 |
|
581 //================ |
|
582 // does not work anymore in 2.13.0 |
|
583 val numbers = List("1", "2").toSet + "3" |
|