progs/lecture5.scala
changeset 455 557d18cce0f0
parent 418 fa7f7144f2bb
child 470 86a456f8cb92
equal deleted inserted replaced
454:289b85843ffd 455:557d18cce0f0
     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"