progs/lecture5.scala
changeset 238 046f37a262d0
parent 226 5e489c9fe47b
child 240 b8cdaf51ffef
equal deleted inserted replaced
237:db4d2fcd8063 238:046f37a262d0
       
     1 // Scala Lecture 5
       
     2 //=================
       
     3 
       
     4 
       
     5 
       
     6 // Laziness with style
       
     7 //=====================
       
     8 
       
     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 
       
    12 
       
    13 def square(x: Int) = x * x
       
    14 
       
    15 square(42 + 8)
       
    16 
       
    17 // this is called strict evaluation
       
    18 
       
    19 // pretty expensive operation
       
    20 def peop(n: BigInt): Boolean = peop(n + 1) 
       
    21 val a = "foo"
       
    22 val b = "foo"
       
    23 
       
    24 if (a == b || peop(0)) println("true") else println("false")
       
    25 
       
    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
       
    30 
       
    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"
       
    37 }
       
    38 
       
    39 
       
    40 // streams (I do not care how many)
       
    41 // primes: 2, 3, 5, 7, 9, 11, 13 ....
       
    42 
       
    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))
       
    47 
       
    48 // the first 10 primes
       
    49 primes.take(10).toList
       
    50 
       
    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)
       
    55 
       
    56 
       
    57 Stream.from(2)
       
    58 Stream.from(2).take(10)
       
    59 Stream.from(2).take(10).print
       
    60 Stream.from(10).take(10).print
       
    61 
       
    62 Stream.from(2).take(10).force
       
    63 
       
    64 // itterative version of the Fibonacci numbers
       
    65 def fibIter(a: BigInt, b: BigInt): Stream[BigInt] =
       
    66   a #:: fibIter(b, a + b)
       
    67 
       
    68 
       
    69 fibIter(1, 1).take(10).force
       
    70 fibIter(8, 13).take(10).force
       
    71 
       
    72 fibIter(1, 1).drop(10000).take(1).print
       
    73 
       
    74 
       
    75 // good for testing
       
    76 
       
    77 
       
    78 // Regular expressions - the power of DSLs in Scala
       
    79 //                                     and Laziness
       
    80 //==================================================
       
    81 
       
    82 abstract class Rexp
       
    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*
       
    89 
       
    90 
       
    91 
       
    92 // writing (ab)* in the format above is 
       
    93 // tedious
       
    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 }
       
   106 implicit def string2rexp(s: String): Rexp = 
       
   107   charlist2rexp(s.toList)
       
   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 
       
   120 
       
   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 
       
   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 
       
   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 
       
   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 }
       
   250 
       
   251 
       
   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"))
       
   271 
       
   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)
       
   291 }
       
   292 
       
   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 
       
   327 
       
   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
       
   341 
       
   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