238
|
1 |
// Scala Lecture 5
|
222
|
2 |
//=================
|
|
3 |
|
|
4 |
|
|
5 |
|
238
|
6 |
// Laziness with style
|
|
7 |
//=====================
|
222
|
8 |
|
238
|
9 |
// The concept of lazy evaluation doesn’t really exist in
|
|
10 |
// non-functional languages, but it is pretty easy to grasp.
|
|
11 |
// Consider first
|
222
|
12 |
|
238
|
13 |
def square(x: Int) = x * x
|
222
|
14 |
|
238
|
15 |
square(42 + 8)
|
222
|
16 |
|
238
|
17 |
// this is called strict evaluation
|
222
|
18 |
|
238
|
19 |
// pretty expensive operation
|
|
20 |
def peop(n: BigInt): Boolean = peop(n + 1)
|
|
21 |
val a = "foo"
|
|
22 |
val b = "foo"
|
222
|
23 |
|
238
|
24 |
if (a == b || peop(0)) println("true") else println("false")
|
222
|
25 |
|
238
|
26 |
// this is called lazy evaluation
|
|
27 |
// you delay compuation until it is really
|
|
28 |
// needed; once calculated though, does not
|
|
29 |
// need to be re-calculated
|
222
|
30 |
|
238
|
31 |
// a useful example is
|
|
32 |
def time_needed[T](i: Int, code: => T) = {
|
|
33 |
val start = System.nanoTime()
|
|
34 |
for (j <- 1 to i) code
|
|
35 |
val end = System.nanoTime()
|
|
36 |
f"${(end - start) / (i * 1.0e9)}%.6f secs"
|
222
|
37 |
}
|
|
38 |
|
|
39 |
|
238
|
40 |
// streams (I do not care how many)
|
|
41 |
// primes: 2, 3, 5, 7, 9, 11, 13 ....
|
222
|
42 |
|
238
|
43 |
def generatePrimes (s: Stream[Int]): Stream[Int] =
|
|
44 |
s.head #:: generatePrimes(s.tail.filter(_ % s.head != 0))
|
|
45 |
|
|
46 |
val primes: Stream[Int] = generatePrimes(Stream.from(2))
|
222
|
47 |
|
238
|
48 |
// the first 10 primes
|
|
49 |
primes.take(10).toList
|
222
|
50 |
|
238
|
51 |
//primes.filter(_ > 100).take(2000).toList
|
|
52 |
|
|
53 |
time_needed(1, primes.filter(_ > 100).take(3000).toList)
|
|
54 |
time_needed(1, primes.filter(_ > 100).take(3000).toList)
|
222
|
55 |
|
|
56 |
|
238
|
57 |
Stream.from(2)
|
|
58 |
Stream.from(2).take(10)
|
|
59 |
Stream.from(2).take(10).print
|
|
60 |
Stream.from(10).take(10).print
|
222
|
61 |
|
238
|
62 |
Stream.from(2).take(10).force
|
222
|
63 |
|
238
|
64 |
// itterative version of the Fibonacci numbers
|
|
65 |
def fibIter(a: BigInt, b: BigInt): Stream[BigInt] =
|
|
66 |
a #:: fibIter(b, a + b)
|
222
|
67 |
|
|
68 |
|
238
|
69 |
fibIter(1, 1).take(10).force
|
|
70 |
fibIter(8, 13).take(10).force
|
|
71 |
|
|
72 |
fibIter(1, 1).drop(10000).take(1).print
|
222
|
73 |
|
|
74 |
|
238
|
75 |
// good for testing
|
222
|
76 |
|
|
77 |
|
|
78 |
// Regular expressions - the power of DSLs in Scala
|
238
|
79 |
// and Laziness
|
222
|
80 |
//==================================================
|
|
81 |
|
|
82 |
abstract class Rexp
|
226
|
83 |
case object ZERO extends Rexp // nothing
|
|
84 |
case object ONE extends Rexp // the empty string
|
|
85 |
case class CHAR(c: Char) extends Rexp // a character c
|
|
86 |
case class ALT(r1: Rexp, r2: Rexp) extends Rexp // alternative r1 + r2
|
|
87 |
case class SEQ(r1: Rexp, r2: Rexp) extends Rexp // sequence r1 . r2
|
|
88 |
case class STAR(r: Rexp) extends Rexp // star r*
|
222
|
89 |
|
|
90 |
|
|
91 |
|
226
|
92 |
// writing (ab)* in the format above is
|
|
93 |
// tedious
|
222
|
94 |
val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
|
|
95 |
|
|
96 |
|
|
97 |
// some convenience for typing in regular expressions
|
|
98 |
import scala.language.implicitConversions
|
|
99 |
import scala.language.reflectiveCalls
|
|
100 |
|
|
101 |
def charlist2rexp(s: List[Char]): Rexp = s match {
|
|
102 |
case Nil => ONE
|
|
103 |
case c::Nil => CHAR(c)
|
|
104 |
case c::s => SEQ(CHAR(c), charlist2rexp(s))
|
|
105 |
}
|
224
|
106 |
implicit def string2rexp(s: String): Rexp =
|
|
107 |
charlist2rexp(s.toList)
|
222
|
108 |
|
|
109 |
|
|
110 |
val r1 = STAR("ab")
|
|
111 |
val r2 = STAR(ALT("ab", "baa baa black sheep"))
|
|
112 |
val r3 = STAR(SEQ("ab", ALT("a", "b")))
|
|
113 |
|
|
114 |
implicit def RexpOps (r: Rexp) = new {
|
|
115 |
def | (s: Rexp) = ALT(r, s)
|
|
116 |
def % = STAR(r)
|
|
117 |
def ~ (s: Rexp) = SEQ(r, s)
|
|
118 |
}
|
|
119 |
|
226
|
120 |
|
222
|
121 |
implicit def stringOps (s: String) = new {
|
|
122 |
def | (r: Rexp) = ALT(s, r)
|
|
123 |
def | (r: String) = ALT(s, r)
|
|
124 |
def % = STAR(s)
|
|
125 |
def ~ (r: Rexp) = SEQ(s, r)
|
|
126 |
def ~ (r: String) = SEQ(s, r)
|
|
127 |
}
|
|
128 |
|
238
|
129 |
|
|
130 |
def depth(r: Rexp) : Int = r match {
|
|
131 |
case ZERO => 0
|
|
132 |
case ONE => 0
|
|
133 |
case CHAR(_) => 0
|
|
134 |
case ALT(r1, r2) => Math.max(depth(r1), depth(r2)) + 1
|
|
135 |
case SEQ(r1, r2) => Math.max(depth(r1), depth(r2)) + 1
|
|
136 |
case STAR(r1) => depth(r1) + 1
|
|
137 |
}
|
|
138 |
|
222
|
139 |
//example regular expressions
|
|
140 |
val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
|
141 |
val sign = "+" | "-" | ""
|
|
142 |
val number = sign ~ digit ~ digit.%
|
|
143 |
|
238
|
144 |
// task: enumerate exhaustively regular expression
|
|
145 |
// starting from small ones towards bigger ones.
|
|
146 |
|
|
147 |
// 1st idea: enumerate them up to a level
|
|
148 |
|
|
149 |
def enuml(l: Int, s: String) : Set[Rexp] = l match {
|
|
150 |
case 0 => Set(ZERO, ONE) ++ s.map(CHAR).toSet
|
|
151 |
case n =>
|
|
152 |
val rs = enuml(n - 1, s)
|
|
153 |
rs ++
|
|
154 |
(for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) ++
|
|
155 |
(for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) ++
|
|
156 |
(for (r1 <- rs) yield STAR(r1))
|
|
157 |
}
|
|
158 |
|
|
159 |
enuml(1, "a").size
|
|
160 |
enuml(2, "a").size
|
|
161 |
enuml(3, "a").size // out of heap space
|
|
162 |
|
|
163 |
|
|
164 |
def enum(rs: Stream[Rexp]) : Stream[Rexp] =
|
|
165 |
rs #::: enum( (for (r1 <- rs; r2 <- rs) yield ALT(r1, r2)) #:::
|
|
166 |
(for (r1 <- rs; r2 <- rs) yield SEQ(r1, r2)) #:::
|
|
167 |
(for (r1 <- rs) yield STAR(r1)) )
|
|
168 |
|
|
169 |
|
|
170 |
enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR)).take(200).force
|
|
171 |
enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR)).take(200000).force
|
|
172 |
|
|
173 |
|
|
174 |
val is =
|
|
175 |
(enum(ZERO #:: ONE #:: "ab".toStream.map(CHAR))
|
|
176 |
.dropWhile(depth(_) < 3)
|
|
177 |
.take(10).foreach(println))
|
|
178 |
|
|
179 |
|
|
180 |
|
|
181 |
// Parsing - The Solved Problem That Isn't
|
|
182 |
//=========================================
|
|
183 |
//
|
|
184 |
// https://tratt.net/laurie/blog/entries/parsing_the_solved_problem_that_isnt.html
|
|
185 |
//
|
|
186 |
// Or, A topic of endless "fun"(?)
|
|
187 |
|
|
188 |
|
|
189 |
// input type: String
|
|
190 |
// output type: Int
|
|
191 |
Integer.parseInt("123456")
|
|
192 |
|
|
193 |
/* Note, in the previous lectures I did not show the type consraint
|
|
194 |
* I <% Seq[_] , which means that the input type I can be
|
|
195 |
* treated, or seen, as a sequence. */
|
|
196 |
|
|
197 |
abstract class Parser[I <% Seq[_], T] {
|
|
198 |
def parse(ts: I): Set[(T, I)]
|
|
199 |
|
|
200 |
def parse_all(ts: I) : Set[T] =
|
|
201 |
for ((head, tail) <- parse(ts);
|
|
202 |
if (tail.isEmpty)) yield head
|
|
203 |
}
|
|
204 |
|
|
205 |
// the idea is that a parser can parse something
|
|
206 |
// from the input and leaves something unparsed => pairs
|
|
207 |
|
|
208 |
class AltParser[I <% Seq[_], T](
|
|
209 |
p: => Parser[I, T],
|
|
210 |
q: => Parser[I, T]) extends Parser[I, T] {
|
|
211 |
|
|
212 |
def parse(sb: I) = p.parse(sb) ++ q.parse(sb)
|
|
213 |
}
|
|
214 |
|
|
215 |
|
|
216 |
class SeqParser[I <% Seq[_], T, S](
|
|
217 |
p: => Parser[I, T],
|
|
218 |
q: => Parser[I, S]) extends Parser[I, (T, S)] {
|
|
219 |
|
|
220 |
def parse(sb: I) =
|
|
221 |
for ((head1, tail1) <- p.parse(sb);
|
|
222 |
(head2, tail2) <- q.parse(tail1)) yield ((head1, head2), tail2)
|
|
223 |
}
|
|
224 |
|
|
225 |
|
|
226 |
class FunParser[I <% Seq[_], T, S](
|
|
227 |
p: => Parser[I, T],
|
|
228 |
f: T => S) extends Parser[I, S] {
|
|
229 |
|
|
230 |
def parse(sb: I) =
|
|
231 |
for ((head, tail) <- p.parse(sb)) yield (f(head), tail)
|
|
232 |
}
|
|
233 |
|
|
234 |
|
|
235 |
implicit def ParserOps[I<% Seq[_], T](p: Parser[I, T]) = new {
|
|
236 |
def | (q : => Parser[I, T]) = new AltParser[I, T](p, q)
|
|
237 |
def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
|
|
238 |
def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
|
|
239 |
}
|
|
240 |
|
|
241 |
implicit def StringOps(s: String) = new {
|
|
242 |
def | (q : => Parser[String, String]) = new AltParser[String, String](s, q)
|
|
243 |
def | (r: String) = new AltParser[String, String](s, r)
|
|
244 |
def ==>[S] (f: => String => S) = new FunParser[String, String, S](s, f)
|
|
245 |
def ~[S] (q : => Parser[String, S]) =
|
|
246 |
new SeqParser[String, String, S](s, q)
|
|
247 |
def ~ (r: String) =
|
|
248 |
new SeqParser[String, String, String](s, r)
|
|
249 |
}
|
222
|
250 |
|
|
251 |
|
238
|
252 |
// atomic parsers
|
|
253 |
case class CharParser(c: Char) extends Parser[String, Char] {
|
|
254 |
def parse(sb: String) =
|
|
255 |
if (sb != "" && sb.head == c) Set((c, sb.tail)) else Set()
|
|
256 |
}
|
|
257 |
|
|
258 |
import scala.util.matching.Regex
|
|
259 |
case class RegexParser(reg: Regex) extends Parser[String, String] {
|
|
260 |
def parse(sb: String) = reg.findPrefixMatchOf(sb) match {
|
|
261 |
case None => Set()
|
|
262 |
case Some(m) => Set((m.matched, m.after.toString))
|
|
263 |
}
|
|
264 |
}
|
|
265 |
|
|
266 |
val NumParser = RegexParser("[0-9]+".r)
|
|
267 |
def StringParser(s: String) = RegexParser(Regex.quote(s).r)
|
|
268 |
|
|
269 |
println(NumParser.parse_all("12345"))
|
|
270 |
println(NumParser.parse_all("12u45"))
|
222
|
271 |
|
238
|
272 |
|
|
273 |
// convenience
|
|
274 |
implicit def string2parser(s: String) = StringParser(s)
|
|
275 |
implicit def char2parser(c: Char) = CharParser(c)
|
|
276 |
|
|
277 |
implicit def ParserOps[I<% Seq[_], T](p: Parser[I, T]) = new {
|
|
278 |
def | (q : => Parser[I, T]) = new AltParser[I, T](p, q)
|
|
279 |
def ==>[S] (f: => T => S) = new FunParser[I, T, S](p, f)
|
|
280 |
def ~[S] (q : => Parser[I, S]) = new SeqParser[I, T, S](p, q)
|
|
281 |
}
|
|
282 |
|
|
283 |
implicit def StringOps(s: String) = new {
|
|
284 |
def | (q : => Parser[String, String]) = new AltParser[String, String](s, q)
|
|
285 |
def | (r: String) = new AltParser[String, String](s, r)
|
|
286 |
def ==>[S] (f: => String => S) = new FunParser[String, String, S](s, f)
|
|
287 |
def ~[S] (q : => Parser[String, S]) =
|
|
288 |
new SeqParser[String, String, S](s, q)
|
|
289 |
def ~ (r: String) =
|
|
290 |
new SeqParser[String, String, String](s, r)
|
222
|
291 |
}
|
|
292 |
|
238
|
293 |
|
|
294 |
val NumParserInt = NumParser ==> (s => s.toInt)
|
|
295 |
|
|
296 |
NumParser.parse_all("12345")
|
|
297 |
NumParserInt.parse_all("12345")
|
|
298 |
NumParserInt.parse_all("12u45")
|
|
299 |
|
|
300 |
|
|
301 |
// grammar for arithmetic expressions
|
|
302 |
//
|
|
303 |
// E ::= T + E | T - E | T
|
|
304 |
// T ::= F * T | F
|
|
305 |
// F ::= ( E ) | Number
|
|
306 |
|
|
307 |
|
|
308 |
lazy val E: Parser[String, Int] =
|
|
309 |
(T ~ "+" ~ E) ==> { case ((x, y), z) => x + z } |
|
|
310 |
(T ~ "-" ~ E) ==> { case ((x, y), z) => x - z } | T
|
|
311 |
lazy val T: Parser[String, Int] =
|
|
312 |
(F ~ "*" ~ T) ==> { case ((x, y), z) => x * z } | F
|
|
313 |
lazy val F: Parser[String, Int] =
|
|
314 |
("(" ~ E ~ ")") ==> { case ((x, y), z) => y } | NumParserInt
|
|
315 |
|
|
316 |
println(E.parse_all("1+3+4"))
|
|
317 |
println(E.parse_all("4*2+3"))
|
|
318 |
println(E.parse_all("4*(2+3)"))
|
|
319 |
println(E.parse_all("(4)*((2+3))"))
|
|
320 |
println(E.parse_all("4/2+3"))
|
|
321 |
println(E.parse_all("(1+2)+3"))
|
|
322 |
println(E.parse_all("1+2+3"))
|
|
323 |
|
|
324 |
|
|
325 |
|
|
326 |
|
222
|
327 |
|
238
|
328 |
// The End ... Almost Christimas
|
|
329 |
//===============================
|
|
330 |
|
|
331 |
// I hope you had fun!
|
|
332 |
|
|
333 |
// A function should do one thing, and only one thing.
|
|
334 |
|
|
335 |
// Make your variables immutable, unless there's a good
|
|
336 |
// reason not to.
|
|
337 |
|
|
338 |
// I did it, but this is actually not a good reason:
|
|
339 |
// generating new labels
|
|
340 |
var counter = -1
|
222
|
341 |
|
238
|
342 |
def Fresh(x: String) = {
|
|
343 |
counter += 1
|
|
344 |
x ++ "_" ++ counter.toString()
|
|
345 |
}
|
|
346 |
|
|
347 |
Fresh("x")
|
|
348 |
Fresh("x")
|
|
349 |
|
|
350 |
|
|
351 |
|
|
352 |
// You can be productive on Day 1, but the language is deep.
|
|
353 |
//
|
|
354 |
// http://scalapuzzlers.com
|
|
355 |
//
|
|
356 |
// http://www.latkin.org/blog/2017/05/02/when-the-scala-compiler-doesnt-help/
|
|
357 |
|
|
358 |
List(1, 2, 3) contains "your mom"
|
|
359 |
|
|
360 |
// I like best about Scala that it lets me often write
|
|
361 |
// concise, readable code. And it hooks up with the
|
|
362 |
// Isabelle theorem prover.
|
|
363 |
|