progs/lecture4.scala
changeset 222 e52cc402caee
parent 218 22705d22c105
child 223 c6453f3547ec
equal deleted inserted replaced
221:9e7897f25e13 222:e52cc402caee
       
     1 // Scala Lecture 4
       
     2 //=================
       
     3 
       
     4 
       
     5 // Polymorphic Types
       
     6 //===================
       
     7 
       
     8 // You do not want to write functions like contains, first, 
       
     9 // length and so on for every type of lists.
       
    10 
       
    11 
       
    12 def length_string_list(lst: List[String]): Int = lst match {
       
    13   case Nil => 0
       
    14   case x::xs => 1 + length_string_list(xs)
       
    15 }
       
    16 
       
    17 def length_int_list(lst: List[Int]): Int = lst match {
       
    18   case Nil => 0
       
    19   case x::xs => 1 + length_int_list(xs)
       
    20 }
       
    21 
       
    22 length_string_list(List("1", "2", "3", "4"))
       
    23 length_int_list(List(1, 2, 3, 4))
       
    24 
       
    25 //-----
       
    26 def length[A](lst: List[A]): Int = lst match {
       
    27   case Nil => 0
       
    28   case x::xs => 1 + length(xs)
       
    29 }
       
    30 length(List("1", "2", "3", "4"))
       
    31 length(List(1, 2, 3, 4))
       
    32 
       
    33 
       
    34 def map[A, B](lst: List[A], f: A => B): List[B] = lst match {
       
    35   case Nil => Nil
       
    36   case x::xs => f(x)::map(xs, f) 
       
    37 }
       
    38 
       
    39 map(List(1, 2, 3, 4), (x: Int) => x * x)
       
    40 
       
    41 
       
    42 // Remember?
       
    43 def first[A, B](xs: List[A], f: A => Option[B]) : Option[B] = ...
       
    44 
       
    45 
       
    46 // distinct / distinctBy
       
    47 
       
    48 val ls = List(1,2,3,3,2,4,3,2,1)
       
    49 ls.distinct
       
    50 
       
    51 
     1 def distinctBy[B, C](xs: List[B], f: B => C, acc: List[C] = Nil): List[B] = xs match {
    52 def distinctBy[B, C](xs: List[B], f: B => C, acc: List[C] = Nil): List[B] = xs match {
     2   case Nil => Nil
    53   case Nil => Nil
     3   case (x::xs) => {
    54   case (x::xs) => {
     4     val res = f(x)
    55     val res = f(x)
     5     if (acc.contains(res)) distinctBy(xs, f, acc)  
    56     if (acc.contains(res)) distinctBy(xs, f, acc)  
     6     else x::distinctBy(xs, f, res::acc)
    57     else x::distinctBy(xs, f, res::acc)
     7   }
    58   }
     8 } 
    59 } 
     9 
    60 
    10 
    61 distinctBy(ls, (x: Int) => x)
    11 
    62 
       
    63 
       
    64 val cs = List('A', 'b', 'a', 'c', 'B', 'D', 'd')
       
    65 
       
    66 distinctBy(cs, (c:Char) => c.toUpper)
       
    67 
       
    68 
       
    69 
       
    70 // Type inference is local in Scala
       
    71 
       
    72 def id[T](x: T) : T = x
       
    73 
       
    74 
       
    75 val x = id(322)          // Int
       
    76 val y = id("hey")        // String
       
    77 val z = id(Set(1,2,3,4)) // Set[Int]
       
    78 
       
    79 
       
    80 
       
    81 // The type variable concept in Scala can get really complicated.
       
    82 //
       
    83 // - variance (OO)
       
    84 // - bounds (subtyping)
       
    85 // - quantification
       
    86 
       
    87 // Java has issues with this too: Java allows
       
    88 // to write the following, but raises an exception
       
    89 // at runtime
       
    90 
       
    91 //Object[] arr = new Integer[10];
       
    92 //arr[0] = "Hello World";
       
    93 
       
    94 
       
    95 // Scala gives you a compile-time error
       
    96 
       
    97 var arr = Array[Int]()
       
    98 arr(0) = "Hello World"
       
    99 
       
   100 
       
   101 
       
   102 
       
   103 
       
   104 
       
   105 //
       
   106 // Object Oriented Programming in Scala
       
   107 //
       
   108 // =====================================
       
   109 
       
   110 abstract class Animal
       
   111 case class Bird(name: String) extends Animal
       
   112 case class Mammal(name: String) extends Animal
       
   113 case class Reptile(name: String) extends Animal
       
   114 
       
   115 println(new Bird("Sparrow"))
       
   116 println(Bird("Sparrow").toString)
       
   117 
       
   118 
       
   119 // you can override methods
       
   120 case class Bird(name: String) extends Animal {
       
   121   override def toString = name
       
   122 }
       
   123 
       
   124 
       
   125 // There is a very convenient short-hand notation
       
   126 // for constructors
       
   127 
       
   128 class Fraction(x: Int, y: Int) {
       
   129   def numer = x
       
   130   def denom = y
       
   131 }
       
   132 
       
   133 
       
   134 case class Fraction(numer: Int, denom: Int)
       
   135 
       
   136 val half = Fraction(1, 2)
       
   137 
       
   138 half.denom
       
   139 
       
   140 
       
   141 // in mandelbrot.scala I used complex (imaginary) numbers and implemented
       
   142 // the usual arithmetic operations for complex numbers
       
   143 
       
   144 case class Complex(re: Double, im: Double) { 
       
   145   // represents the complex number re + im * i
       
   146   def +(that: Complex) = Complex(this.re + that.re, this.im + that.im)
       
   147   def -(that: Complex) = Complex(this.re - that.re, this.im - that.im)
       
   148   def *(that: Complex) = Complex(this.re * that.re - this.im * that.im,
       
   149                                  this.re * that.im + that.re * this.im)
       
   150   def *(that: Double) = Complex(this.re * that, this.im * that)
       
   151   def abs = Math.sqrt(this.re * this.re + this.im * this.im)
       
   152 }
       
   153 
       
   154 val test = Complex(1, 2) + Complex (3, 4)
       
   155 
       
   156 // this could have equally been written as
       
   157 val test = Complex(1, 2).+(Complex (3, 4))
       
   158 
       
   159 // this applies to all methods, but requires
       
   160 import scala.language.postfixOps
       
   161 
       
   162 List(5, 2, 3, 4).sorted
       
   163 List(5, 2, 3, 4) sorted
       
   164 
       
   165 
       
   166 // to allow the notation n + m * i
       
   167 import scala.language.implicitConversions   
       
   168 object i extends Complex(0, 1)
       
   169 implicit def double2complex(re: Double) = Complex(re, 0)
       
   170 
       
   171 
       
   172 val inum1 = -2.0 + -1.5 * i
       
   173 val inum2 =  1.0 +  1.5 * i
       
   174 
       
   175 
       
   176 
       
   177 // all is public by default....so no public
       
   178 // you can have the usual restrictions about private values
       
   179 // and methods, if you are MUTABLE(!!!)
       
   180 
       
   181 case class BankAccount(init: Int) {
       
   182 
       
   183   private var balance = init
       
   184 
       
   185   def deposit(amount: Int): Unit = {
       
   186     if (amount > 0) balance = balance + amount
       
   187   }
       
   188 
       
   189   def withdraw(amount: Int): Int =
       
   190     if (0 < amount && amount <= balance) {
       
   191       balance = balance - amount
       
   192       balance
       
   193     } else throw new Error("insufficient funds")
       
   194 }
       
   195 
       
   196 // BUT since we are IMMUTABLE, this is virtually of not 
       
   197 // concern to us.
       
   198 
       
   199 
       
   200 
       
   201 
       
   202 
       
   203 // DFAs in Scala  
       
   204 import scala.util.Try
       
   205 
       
   206 
       
   207 // A is the state type
       
   208 // C is the input (usually characters)
       
   209 
       
   210 case class DFA[A, C](start: A,               // starting state
       
   211                      delta: (A, C) => A,     // transition function
       
   212                      fins:  A => Boolean) {  // final states
       
   213 
       
   214   def deltas(q: A, s: List[C]) : A = s match {
       
   215     case Nil => q
       
   216     case c::cs => deltas(delta(q, c), cs)
       
   217   }
       
   218 
       
   219   def accepts(s: List[C]) : Boolean = 
       
   220     Try(fins(deltas(start, s))) getOrElse false
       
   221 }
       
   222 
       
   223 // the example shown in the handout 
       
   224 abstract class State
       
   225 case object Q0 extends State
       
   226 case object Q1 extends State
       
   227 case object Q2 extends State
       
   228 case object Q3 extends State
       
   229 case object Q4 extends State
       
   230 
       
   231 val delta : (State, Char) => State = 
       
   232   { case (Q0, 'a') => Q1
       
   233     case (Q0, 'b') => Q2
       
   234     case (Q1, 'a') => Q4
       
   235     case (Q1, 'b') => Q2
       
   236     case (Q2, 'a') => Q3
       
   237     case (Q2, 'b') => Q2
       
   238     case (Q3, 'a') => Q4
       
   239     case (Q3, 'b') => Q0
       
   240     case (Q4, 'a') => Q4
       
   241     case (Q4, 'b') => Q4 
       
   242     case _ => throw new Exception("Undefined") }
       
   243 
       
   244 val dfa = DFA(Q0, delta, Set[State](Q4))
       
   245 
       
   246 dfa.accepts("abaaa".toList)     // true
       
   247 dfa.accepts("bbabaab".toList)   // true
       
   248 dfa.accepts("baba".toList)      // false
       
   249 dfa.accepts("abc".toList)       // false
       
   250 
       
   251 // another DFA test with a Sink state
       
   252 abstract class S
       
   253 case object S0 extends S
       
   254 case object S1 extends S
       
   255 case object S2 extends S
       
   256 case object Sink extends S
       
   257 
       
   258 // transition function with a sink state
       
   259 val sigma : (S, Char) :=> S = 
       
   260   { case (S0, 'a') => S1
       
   261     case (S1, 'a') => S2
       
   262     case _ => Sink
       
   263   }
       
   264 
       
   265 val dfa2 = DFA(S0, sigma, Set[S](S2))
       
   266 
       
   267 dfa2.accepts("aa".toList)        // true
       
   268 dfa2.accepts("".toList)          // false
       
   269 dfa2.accepts("ab".toList)        // false
       
   270 
       
   271 
       
   272 
       
   273 
       
   274 // NFAs (Nondeterministic Finite Automata)
       
   275 
       
   276 
       
   277 case class NFA[A, C](starts: Set[A],           // starting states
       
   278                      delta: (A, C) => Set[A],  // transition function
       
   279                      fins:  A => Boolean) {    // final states 
       
   280 
       
   281   // given a state and a character, what is the set of 
       
   282   // next states? if there is none => empty set
       
   283   def next(q: A, c: C) : Set[A] = 
       
   284     Try(delta(q, c)) getOrElse Set[A]() 
       
   285 
       
   286   def nexts(qs: Set[A], c: C) : Set[A] =
       
   287     qs.flatMap(next(_, c))
       
   288 
       
   289   // depth-first version of accepts
       
   290   def search(q: A, s: List[C]) : Boolean = s match {
       
   291     case Nil => fins(q)
       
   292     case c::cs => next(q, c).exists(search(_, cs))
       
   293   }
       
   294 
       
   295   def accepts(s: List[C]) : Boolean =
       
   296     starts.exists(search(_, s))
       
   297 }
       
   298 
       
   299 
       
   300 
       
   301 // NFA examples
       
   302 
       
   303 val nfa_trans1 : (State, Char) => Set[State] = 
       
   304   { case (Q0, 'a') => Set(Q0, Q1) 
       
   305     case (Q0, 'b') => Set(Q2) 
       
   306     case (Q1, 'a') => Set(Q1) 
       
   307     case (Q2, 'b') => Set(Q2) }
       
   308 
       
   309 val nfa = NFA(Set[State](Q0), nfa_trans1, Set[State](Q2))
       
   310 
       
   311 nfa.accepts("aa".toList)             // false
       
   312 nfa.accepts("aaaaa".toList)          // false
       
   313 nfa.accepts("aaaaab".toList)         // true
       
   314 nfa.accepts("aaaaabbb".toList)       // true
       
   315 nfa.accepts("aaaaabbbaaa".toList)    // false
       
   316 nfa.accepts("ac".toList)             // false
       
   317 
       
   318 
       
   319 // Q: Why the kerfuffle about the polymorphic types in DFAs/NFAs
       
   320 // A: Subset construction
       
   321 
       
   322 def subset[A, C](nfa: NFA[A, C]) : DFA[Set[A], C] = {
       
   323   DFA(nfa.starts, 
       
   324       { case (qs, c) => nfa.nexts(qs, c) }, 
       
   325       _.exists(nfa.fins))
       
   326 }
       
   327 
       
   328 subset(nfa1).accepts("aa".toList)             // false
       
   329 subset(nfa1).accepts("aaaaa".toList)          // false
       
   330 subset(nfa1).accepts("aaaaab".toList)         // true
       
   331 subset(nfa1).accepts("aaaaabbb".toList)       // true
       
   332 subset(nfa1).accepts("aaaaabbbaaa".toList)    // false
       
   333 subset(nfa1).accepts("ac".toList)             // false
       
   334 
       
   335 
       
   336 
       
   337 
       
   338 
       
   339 
       
   340 
       
   341 // Cool Stuff in Scala
       
   342 //=====================
       
   343 
       
   344 
       
   345 // Implicits or How to Pimp my Library
       
   346 //=====================================
       
   347 //
       
   348 // For example adding your own methods to Strings:
       
   349 // Imagine you want to increment strings, like
       
   350 //
       
   351 //     "HAL".increment
       
   352 //
       
   353 // you can avoid ugly fudges, like a MyString, by
       
   354 // using implicit conversions.
       
   355 
       
   356 
       
   357 implicit class MyString(s: String) {
       
   358   def increment = for (c <- s) yield (c + 1).toChar 
       
   359 }
       
   360 
       
   361 "HAL".increment
       
   362 
       
   363 
       
   364 
       
   365 
       
   366 // Regular expressions - the power of DSLs in Scala
       
   367 //==================================================
       
   368 
       
   369 abstract class Rexp
       
   370 case object ZERO extends Rexp                       // nothing
       
   371 case object ONE extends Rexp                        // the empty string
       
   372 case class CHAR(c: Char) extends Rexp               // a character c
       
   373 case class ALT(r1: Rexp, r2: Rexp) extends Rexp     // alternative  r1 + r2
       
   374 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp     // sequence     r1 . r2  
       
   375 case class STAR(r: Rexp) extends Rexp               // star         r*
       
   376 
       
   377 
       
   378 
       
   379 // (ab)*
       
   380 val r0 = STAR(SEQ(CHAR('a'), CHAR('b')))
       
   381 
       
   382 
       
   383 // some convenience for typing in regular expressions
       
   384 import scala.language.implicitConversions    
       
   385 import scala.language.reflectiveCalls 
       
   386 
       
   387 def charlist2rexp(s: List[Char]): Rexp = s match {
       
   388   case Nil => ONE
       
   389   case c::Nil => CHAR(c)
       
   390   case c::s => SEQ(CHAR(c), charlist2rexp(s))
       
   391 }
       
   392 implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList)
       
   393 
       
   394 
       
   395 val r1 = STAR("ab")
       
   396 val r2 = STAR(ALT("ab", "baa baa black sheep"))
       
   397 val r3 = STAR(SEQ("ab", ALT("a", "b")))
       
   398 
       
   399 implicit def RexpOps (r: Rexp) = new {
       
   400   def | (s: Rexp) = ALT(r, s)
       
   401   def % = STAR(r)
       
   402   def ~ (s: Rexp) = SEQ(r, s)
       
   403 }
       
   404 
       
   405 implicit def stringOps (s: String) = new {
       
   406   def | (r: Rexp) = ALT(s, r)
       
   407   def | (r: String) = ALT(s, r)
       
   408   def % = STAR(s)
       
   409   def ~ (r: Rexp) = SEQ(s, r)
       
   410   def ~ (r: String) = SEQ(s, r)
       
   411 }
       
   412 
       
   413 //example regular expressions
       
   414 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
       
   415 val sign = "+" | "-" | ""
       
   416 val number = sign ~ digit ~ digit.% 
       
   417 
       
   418 
       
   419 
       
   420 // Lazy Evaluation
       
   421 //=================
       
   422 //
       
   423 // do not evaluate arguments just yet
       
   424 
       
   425 def time_needed[T](i: Int, code: => T) = {
       
   426   val start = System.nanoTime()
       
   427   for (j <- 1 to i) code
       
   428   val end = System.nanoTime()
       
   429   (end - start)/(i * 1.0e9)
       
   430 }
       
   431 
       
   432 // same examples using the internal regexes
       
   433 val evil = "(a*)*b"
       
   434 
       
   435 ("a" * 10 ++ "b").matches(evil)
       
   436 ("a" * 10).matches(evil)
       
   437 ("a" * 10000).matches(evil)
       
   438 ("a" * 20000).matches(evil)
       
   439 
       
   440 time_needed(2, ("a" * 10000).matches(evil))