5 |
5 |
6 // Laziness with style |
6 // Laziness with style |
7 //===================== |
7 //===================== |
8 |
8 |
9 // The concept of lazy evaluation doesn’t really |
9 // The concept of lazy evaluation doesn’t really |
10 // exist in non-functional languages, but it is |
10 // exist in non-functional languages. C-like languages |
11 // pretty easy to grasp. Consider first |
11 // are strict. To see the difference, consider |
12 |
12 |
13 def square(x: Int) = x * x |
13 def square(x: Int) = x * x |
14 |
14 |
15 square(42 + 8) |
15 square(42 + 8) |
16 |
16 |
17 // this is called strict evaluation |
17 // This is called "strict evaluation". |
18 |
18 |
19 // say we have a pretty expensive operation |
19 // In contrast, say we have a pretty expensive operation: |
|
20 |
20 def peop(n: BigInt): Boolean = peop(n + 1) |
21 def peop(n: BigInt): Boolean = peop(n + 1) |
21 |
22 |
22 val a = "foo" |
23 val a = "foo" |
23 val b = "bar" |
24 val b = "foo" |
24 |
25 |
25 if (a == b || peop(0)) println("true") else println("false") |
26 if (a == b || peop(0)) println("true") else println("false") |
26 |
27 |
27 // this is called lazy evaluation |
28 // This is called "lazy evaluation": |
28 // you delay compuation until it is really |
29 // you delay compuation until it is really |
29 // needed; once calculated though, does not |
30 // needed. Once calculated though, the result |
30 // need to be re-calculated |
31 // does not need to be re-calculated. |
31 |
32 |
32 // a useful example is |
33 // A useful example is |
33 def time_needed[T](i: Int, code: => T) = { |
34 def time_needed[T](i: Int, code: => T) = { |
34 val start = System.nanoTime() |
35 val start = System.nanoTime() |
35 for (j <- 1 to i) code |
36 for (j <- 1 to i) code |
36 val end = System.nanoTime() |
37 val end = System.nanoTime() |
37 f"${(end - start) / (i * 1.0e9)}%.6f secs" |
38 f"${(end - start) / (i * 1.0e9)}%.6f secs" |
38 } |
39 } |
39 |
40 |
40 |
41 // A slightly less obvious example: Prime Numbers. |
41 // streams (I do not care how many) |
42 // (I do not care how many) primes: 2, 3, 5, 7, 9, 11, 13 .... |
42 // primes: 2, 3, 5, 7, 9, 11, 13 .... |
43 |
43 |
44 def generatePrimes (s: LazyList[Int]): LazyList[Int] = |
44 def generatePrimes (s: Stream[Int]): Stream[Int] = |
|
45 s.head #:: generatePrimes(s.tail.filter(_ % s.head != 0)) |
45 s.head #:: generatePrimes(s.tail.filter(_ % s.head != 0)) |
46 |
46 |
47 val primes = generatePrimes(Stream.from(2)) |
47 val primes = generatePrimes(LazyList.from(2)) |
48 |
48 |
49 // the first 10 primes |
49 // the first 10 primes |
50 primes.take(10).par.toList |
50 primes.take(10).toList |
51 |
51 |
52 time_needed(1, primes.filter(_ > 100).take(3000).toList) |
52 time_needed(1, primes.filter(_ > 100).take(3000).toList) |
53 time_needed(1, primes.filter(_ > 100).take(1000).toList) |
53 time_needed(1, primes.filter(_ > 100).take(3000).toList) |
54 |
54 |
55 // a stream of successive numbers |
55 // A Stream (LazyList) of successive numbers: |
56 |
56 |
57 Stream.from(2).print |
57 LazyList.from(2).take(10) |
58 Stream.from(2).take(10).force |
58 LazyList.from(2).take(10).force |
59 Stream.from(2).take(10).print |
59 |
60 Stream.from(10).take(10).print |
60 // An Iterative version of the Fibonacci numbers |
61 |
61 def fibIter(a: BigInt, b: BigInt): LazyList[BigInt] = |
62 Stream.from(2).take(10).force |
|
63 |
|
64 // iterative version of the Fibonacci numbers |
|
65 def fibIter(a: BigInt, b: BigInt): Stream[BigInt] = |
|
66 a #:: fibIter(b, a + b) |
62 a #:: fibIter(b, a + b) |
67 |
63 |
68 |
64 |
69 fibIter(1, 1).take(10).force |
65 fibIter(1, 1).take(10).force |
70 fibIter(8, 13).take(10).force |
66 fibIter(8, 13).take(10).force |
71 |
67 |
72 fibIter(1, 1).drop(10000).take(1).print |
68 fibIter(1, 1).drop(10000).take(1) |
73 |
69 fibIter(1, 1).drop(10000).take(1).force |
74 |
70 |
75 // good for testing |
71 |
|
72 // LazyLists are good for testing |
76 |
73 |
77 |
74 |
78 // Regular expressions - the power of DSLs in Scala |
75 // Regular expressions - the power of DSLs in Scala |
79 // and Laziness |
76 // and Laziness |
80 //================================================== |
77 //================================================== |
147 } |
143 } |
148 |
144 |
149 enuml(1, "a") |
145 enuml(1, "a") |
150 enuml(1, "a").size |
146 enuml(1, "a").size |
151 enuml(2, "a").size |
147 enuml(2, "a").size |
152 enuml(3, "a").size |
148 enuml(3, "a").size // out of heap space |
153 enuml(4, "a").size // out of heap space |
149 |
154 |
150 |
155 |
151 |
156 def enum(rs: Stream[Rexp]) : Stream[Rexp] = |
152 def enum(rs: LazyList[Rexp]) : LazyList[Rexp] = |
157 rs #::: enum( (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) #::: |
153 rs #::: enum( (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) #::: |
158 (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) #::: |
154 (for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) #::: |
159 (for (r1 <- rs) yield STAR(r1)) ) |
155 (for (r1 <- rs) yield STAR(r1)) ) |
160 |
156 |
161 |
157 |
162 enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR)).take(200).force |
158 enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(200).force |
163 enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR)).take(5000000) |
159 enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))).take(5000000) |
164 |
160 |
165 |
161 |
166 val is = |
162 val is = |
167 (enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR)) |
163 (enum(LazyList(ZERO, ONE, CHAR('a'), CHAR('b'))) |
168 .dropWhile(depth(_) < 3) |
164 .dropWhile(depth(_) < 3) |
169 .take(10).foreach(println)) |
165 .take(10).foreach(println)) |
170 |
166 |
171 |
167 |
172 |
168 // Polymorphic Types |
173 // Parsing - The Solved Problem That Isn't |
169 //=================== |
174 //========================================= |
170 |
|
171 // You do not want to write functions like contains, first, |
|
172 // length and so on for every type of lists. |
|
173 |
|
174 |
|
175 def length_string_list(lst: List[String]): Int = lst match { |
|
176 case Nil => 0 |
|
177 case x::xs => 1 + length_string_list(xs) |
|
178 } |
|
179 |
|
180 def length_int_list(lst: List[Int]): Int = lst match { |
|
181 case Nil => 0 |
|
182 case x::xs => 1 + length_int_list(xs) |
|
183 } |
|
184 |
|
185 length_string_list(List("1", "2", "3", "4")) |
|
186 length_int_list(List(1, 2, 3, 4)) |
|
187 |
|
188 // you can make the function parametric in type(s) |
|
189 |
|
190 def length[A](lst: List[A]): Int = lst match { |
|
191 case Nil => 0 |
|
192 case x::xs => 1 + length(xs) |
|
193 } |
|
194 length(List("1", "2", "3", "4")) |
|
195 length(List(1, 2, 3, 4)) |
|
196 |
|
197 |
|
198 def map[A, B](lst: List[A], f: A => B): List[B] = lst match { |
|
199 case Nil => Nil |
|
200 case x::xs => f(x)::map(xs, f) |
|
201 } |
|
202 |
|
203 map(List(1, 2, 3, 4), (x: Int) => x.toString) |
|
204 |
|
205 |
|
206 |
|
207 // distinct / distinctBy |
|
208 |
|
209 val ls = List(1,2,3,3,2,4,3,2,1) |
|
210 ls.distinct |
|
211 |
|
212 // .minBy(_._2) |
|
213 // .sortBy(_._1) |
|
214 |
|
215 def distinctBy[B, C](xs: List[B], |
|
216 f: B => C, |
|
217 acc: List[C] = Nil): List[B] = xs match { |
|
218 case Nil => Nil |
|
219 case x::xs => { |
|
220 val res = f(x) |
|
221 if (acc.contains(res)) distinctBy(xs, f, acc) |
|
222 else x::distinctBy(xs, f, res::acc) |
|
223 } |
|
224 } |
|
225 |
|
226 val cs = List('A', 'b', 'a', 'c', 'B', 'D', 'd') |
|
227 |
|
228 distinctBy(cs, (c:Char) => c.toUpper) |
|
229 |
|
230 // since 2.13 |
|
231 |
|
232 cs.distinctBy((c:Char) => c.toUpper) |
|
233 |
|
234 |
|
235 // Type inference is local in Scala |
|
236 |
|
237 def id[T](x: T) : T = x |
|
238 |
|
239 val x = id(322) // Int |
|
240 val y = id("hey") // String |
|
241 val z = id(Set(1,2,3,4)) // Set[Int] |
|
242 |
|
243 |
|
244 |
|
245 // The type variable concept in Scala can get really complicated. |
175 // |
246 // |
176 // https://tratt.net/laurie/blog/entries/parsing_the_solved_problem_that_isnt.html |
247 // - variance (OO) |
|
248 // - bounds (subtyping) |
|
249 // - quantification |
|
250 |
|
251 // Java has issues with this too: Java allows |
|
252 // to write the following incorrect code, and |
|
253 // only recovers by raising an exception |
|
254 // at runtime. |
|
255 |
|
256 // Object[] arr = new Integer[10]; |
|
257 // arr[0] = "Hello World"; |
|
258 |
|
259 |
|
260 // Scala gives you a compile-time error, which |
|
261 // is much better. |
|
262 |
|
263 var arr = Array[Int]() |
|
264 arr(0) = "Hello World" |
|
265 |
|
266 |
|
267 |
|
268 // (Immutable) |
|
269 // Object Oriented Programming in Scala |
177 // |
270 // |
178 // Or, A topic of endless "fun"(?) |
271 // ===================================== |
179 |
272 |
180 |
273 abstract class Animal |
181 // input type: String |
274 case class Bird(name: String) extends Animal { |
182 // output type: Int |
275 override def toString = name |
183 Integer.parseInt("123u456") |
276 } |
184 |
277 case class Mammal(name: String) extends Animal |
185 /* Note, in the previous lectures I did not show the type consraint |
278 case class Reptile(name: String) extends Animal |
186 * I <% Seq[_] , which means that the input type I can be |
279 |
187 * treated, or seen, as a sequence. */ |
280 Mammal("Zebra") |
188 |
281 println(Mammal("Zebra")) |
189 abstract class Parser[I <% Seq[_], T] { |
282 println(Mammal("Zebra").toString) |
190 def parse(ts: I): Set[(T, I)] |
283 |
191 |
284 |
192 def parse_all(ts: I) : Set[T] = |
285 Bird("Sparrow") |
193 for ((head, tail) <- parse(ts); |
286 println(Bird("Sparrow")) |
194 if (tail.isEmpty)) yield head |
287 println(Bird("Sparrow").toString) |
195 } |
288 |
196 |
289 |
197 // the idea is that a parser can parse something |
290 // There is a very convenient short-hand notation |
198 // from the input and leaves something unparsed => pairs |
291 // for constructors: |
199 |
292 |
200 class AltParser[I <% Seq[_], T]( |
293 class Fraction(x: Int, y: Int) { |
201 p: => Parser[I, T], |
294 def numer = x |
202 q: => Parser[I, T]) extends Parser[I, T] { |
295 def denom = y |
203 |
296 } |
204 def parse(sb: I) = p.parse(sb) ++ q.parse(sb) |
297 |
205 } |
298 val half = new Fraction(1, 2) |
206 |
299 |
207 |
300 case class Fraction(numer: Int, denom: Int) |
208 class SeqParser[I <% Seq[_], T, S]( |
301 |
209 p: => Parser[I, T], |
302 val half = Fraction(1, 2) |
210 q: => Parser[I, S]) extends Parser[I, (T, S)] { |
303 |
211 |
304 half.denom |
212 def parse(sb: I) = |
305 |
213 for ((head1, tail1) <- p.parse(sb); |
306 |
214 (head2, tail2) <- q.parse(tail1)) yield ((head1, head2), tail2) |
307 // In mandelbrot.scala I used complex (imaginary) numbers |
215 } |
308 // and implemented the usual arithmetic operations for complex |
216 |
309 // numbers. |
217 |
310 |
218 class FunParser[I <% Seq[_], T, S]( |
311 case class Complex(re: Double, im: Double) { |
219 p: => Parser[I, T], |
312 // represents the complex number re + im * i |
220 f: T => S) extends Parser[I, S] { |
313 def +(that: Complex) = Complex(this.re + that.re, this.im + that.im) |
221 |
314 def -(that: Complex) = Complex(this.re - that.re, this.im - that.im) |
222 def parse(sb: I) = |
315 def *(that: Complex) = Complex(this.re * that.re - this.im * that.im, |
223 for ((head, tail) <- p.parse(sb)) yield (f(head), tail) |
316 this.re * that.im + that.re * this.im) |
224 } |
317 def *(that: Double) = Complex(this.re * that, this.im * that) |
225 |
318 def abs = Math.sqrt(this.re * this.re + this.im * this.im) |
226 |
319 } |
227 // atomic parsers |
320 |
228 case class CharParser(c: Char) extends Parser[String, Char] { |
321 val test = Complex(1, 2) + Complex (3, 4) |
229 def parse(sb: String) = |
322 |
230 if (sb != "" && sb.head == c) Set((c, sb.tail)) else Set() |
323 // this could have equally been written as |
231 } |
324 val test = Complex(1, 2).+(Complex (3, 4)) |
232 |
325 |
233 import scala.util.matching.Regex |
326 // this applies to all methods, but requires |
234 case class RegexParser(reg: Regex) extends Parser[String, String] { |
327 import scala.language.postfixOps |
235 def parse(sb: String) = reg.findPrefixMatchOf(sb) match { |
328 |
236 case None => Set() |
329 List(5, 2, 3, 4).sorted |
237 case Some(m) => Set((m.matched, m.after.toString)) |
330 List(5, 2, 3, 4) sorted |
|
331 |
|
332 |
|
333 // ...to allow the notation n + m * i |
|
334 import scala.language.implicitConversions |
|
335 |
|
336 val i = Complex(0, 1) |
|
337 implicit def double2complex(re: Double) = Complex(re, 0) |
|
338 |
|
339 |
|
340 val inum1 = -2.0 + -1.5 * i |
|
341 val inum2 = 1.0 + 1.5 * i |
|
342 |
|
343 |
|
344 |
|
345 // All is public by default....so no public is needed. |
|
346 // You can have the usual restrictions about private |
|
347 // values and methods, if you are MUTABLE !!! |
|
348 |
|
349 case class BankAccount(init: Int) { |
|
350 |
|
351 private var balance = init |
|
352 |
|
353 def deposit(amount: Int): Unit = { |
|
354 if (amount > 0) balance = balance + amount |
238 } |
355 } |
239 } |
356 |
240 |
357 def withdraw(amount: Int): Int = |
241 val NumParser = RegexParser("[0-9]+".r) |
358 if (0 < amount && amount <= balance) { |
242 def StringParser(s: String) = RegexParser(Regex.quote(s).r) |
359 balance = balance - amount |
243 |
360 balance |
244 NumParser.parse_all("12u345") |
361 } else throw new Error("insufficient funds") |
245 println(NumParser.parse_all("12u45")) |
362 } |
246 |
363 |
247 |
364 // BUT since we are completely IMMUTABLE, this is |
248 // convenience |
365 // virtually of not concern to us. |
249 implicit def string2parser(s: String) = StringParser(s) |
366 |
250 implicit def char2parser(c: Char) = CharParser(c) |
367 |
251 |
368 |
252 implicit def ParserOps[I<% Seq[_], T](p: Parser[I, T]) = new { |
369 // another example about Fractions |
253 def | (q : => Parser[I, T]) = new AltParser[I, T](p, q) |
370 import scala.language.implicitConversions |
254 def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f) |
371 import scala.language.reflectiveCalls |
255 def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q) |
372 |
256 } |
373 |
257 |
374 case class Fraction(numer: Int, denom: Int) { |
258 implicit def StringOps(s: String) = new { |
375 override def toString = numer.toString + "/" + denom.toString |
259 def | (q : => Parser[String, String]) = new AltParser[String, String](s, q) |
376 |
260 def | (r: String) = new AltParser[String, String](s, r) |
377 def +(other: Fraction) = Fraction(numer + other.numer, denom + other.denom) |
261 def ==>[S] (f: => String => S) = new FunParser[String, String, S](s, f) |
378 def /(other: Fraction) = Fraction(numer * other.denom, denom * other.numer) |
262 def ~[S] (q : => Parser[String, S]) = |
379 } |
263 new SeqParser[String, String, S](s, q) |
380 |
264 def ~ (r: String) = |
381 implicit def Int2Fraction(x: Int) = Fraction(x, 1) |
265 new SeqParser[String, String, String](s, r) |
382 |
266 } |
383 |
267 |
384 val half = Fraction(1, 2) |
268 |
385 val third = Fraction (1, 3) |
269 val NumParserInt = NumParser ==> (s => 2 * s.toInt) |
386 |
270 |
387 half + third |
271 NumParser.parse_all("12345") |
388 half / third |
272 NumParserInt.parse_all("12345") |
389 |
273 NumParserInt.parse_all("12u45") |
390 (1 / 3) + half |
274 |
391 (1 / 2) + third |
275 |
392 |
276 // grammar for arithmetic expressions |
393 |
277 // |
394 |
278 // E ::= T + E | T - E | T |
395 |
279 // T ::= F * T | F |
396 // DFAs in Scala |
280 // F ::= ( E ) | Number |
397 //=============== |
281 |
398 import scala.util.Try |
282 |
399 |
283 lazy val E: Parser[String, Int] = |
400 |
284 (T ~ "+" ~ E) ==> { case ((x, y), z) => x + z } | |
401 // A is the state type |
285 (T ~ "-" ~ E) ==> { case ((x, y), z) => x - z } | T |
402 // C is the input (usually characters) |
286 lazy val T: Parser[String, Int] = |
403 |
287 (F ~ "*" ~ T) ==> { case ((x, y), z) => x * z } | F |
404 case class DFA[A, C](start: A, // starting state |
288 lazy val F: Parser[String, Int] = |
405 delta: (A, C) => A, // transition function |
289 ("(" ~ E ~ ")") ==> { case ((x, y), z) => y } | NumParserInt |
406 fins: A => Boolean) { // final states (Set) |
290 |
407 |
291 |
408 def deltas(q: A, s: List[C]) : A = s match { |
292 println(E.parse_all("4*2+3")) |
409 case Nil => q |
293 println(E.parse_all("4*(2+3)")) |
410 case c::cs => deltas(delta(q, c), cs) |
294 println(E.parse_all("(4)*((2+3))")) |
411 } |
295 println(E.parse_all("4/2+3")) |
412 |
296 println(E.parse_all("(1+2)+3")) |
413 def accepts(s: List[C]) : Boolean = |
297 println(E.parse_all("1+2+3")) |
414 Try(fins(deltas(start, s))) getOrElse false |
298 |
415 } |
299 |
416 |
|
417 // the example shown in the handout |
|
418 abstract class State |
|
419 case object Q0 extends State |
|
420 case object Q1 extends State |
|
421 case object Q2 extends State |
|
422 case object Q3 extends State |
|
423 case object Q4 extends State |
|
424 |
|
425 val delta : (State, Char) => State = |
|
426 { case (Q0, 'a') => Q1 |
|
427 case (Q0, 'b') => Q2 |
|
428 case (Q1, 'a') => Q4 |
|
429 case (Q1, 'b') => Q2 |
|
430 case (Q2, 'a') => Q3 |
|
431 case (Q2, 'b') => Q2 |
|
432 case (Q3, 'a') => Q4 |
|
433 case (Q3, 'b') => Q0 |
|
434 case (Q4, 'a') => Q4 |
|
435 case (Q4, 'b') => Q4 |
|
436 case _ => throw new Exception("Undefined") } |
|
437 |
|
438 val dfa = DFA(Q0, delta, Set[State](Q4)) |
|
439 |
|
440 dfa.accepts("abaaa".toList) // true |
|
441 dfa.accepts("bbabaab".toList) // true |
|
442 dfa.accepts("baba".toList) // false |
|
443 dfa.accepts("abc".toList) // false |
|
444 |
|
445 |
|
446 // NFAs (Nondeterministic Finite Automata) |
|
447 |
|
448 |
|
449 case class NFA[A, C](starts: Set[A], // starting states |
|
450 delta: (A, C) => Set[A], // transition function |
|
451 fins: A => Boolean) { // final states |
|
452 |
|
453 // given a state and a character, what is the set of |
|
454 // next states? if there is none => empty set |
|
455 def next(q: A, c: C) : Set[A] = |
|
456 Try(delta(q, c)) getOrElse Set[A]() |
|
457 |
|
458 def nexts(qs: Set[A], c: C) : Set[A] = |
|
459 qs.flatMap(next(_, c)) |
|
460 |
|
461 // depth-first version of accepts |
|
462 def search(q: A, s: List[C]) : Boolean = s match { |
|
463 case Nil => fins(q) |
|
464 case c::cs => next(q, c).exists(search(_, cs)) |
|
465 } |
|
466 |
|
467 def accepts(s: List[C]) : Boolean = |
|
468 starts.exists(search(_, s)) |
|
469 } |
|
470 |
|
471 |
|
472 |
|
473 // NFA examples |
|
474 |
|
475 val nfa_trans1 : (State, Char) => Set[State] = |
|
476 { case (Q0, 'a') => Set(Q0, Q1) |
|
477 case (Q0, 'b') => Set(Q2) |
|
478 case (Q1, 'a') => Set(Q1) |
|
479 case (Q2, 'b') => Set(Q2) } |
|
480 |
|
481 val nfa = NFA(Set[State](Q0), nfa_trans1, Set[State](Q2)) |
|
482 |
|
483 nfa.accepts("aa".toList) // false |
|
484 nfa.accepts("aaaaa".toList) // false |
|
485 nfa.accepts("aaaaab".toList) // true |
|
486 nfa.accepts("aaaaabbb".toList) // true |
|
487 nfa.accepts("aaaaabbbaaa".toList) // false |
|
488 nfa.accepts("ac".toList) // false |
|
489 |
|
490 |
|
491 // Q: Why the kerfuffle about the polymorphic types in DFAs/NFAs? |
|
492 // A: Subset construction. Here the state type for the DFA is |
|
493 // sets of states. |
|
494 |
|
495 def subset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = { |
|
496 DFA(nfa.starts, |
|
497 { case (qs, c) => nfa.nexts(qs, c) }, |
|
498 _.exists(nfa.fins)) |
|
499 } |
|
500 |
|
501 subset(nfa).accepts("aa".toList) // false |
|
502 subset(nfa).accepts("aaaaa".toList) // false |
|
503 subset(nfa).accepts("aaaaab".toList) // true |
|
504 subset(nfa).accepts("aaaaabbb".toList) // true |
|
505 subset(nfa).accepts("aaaaabbbaaa".toList) // false |
|
506 subset(nfa).accepts("ac".toList) // false |
300 |
507 |
301 |
508 |
302 |
509 |
303 // The End ... Almost Christmas |
510 // The End ... Almost Christmas |
304 //=============================== |
511 //=============================== |