progs/lecture3.scala
changeset 67 ca5884c2e3bd
parent 53 9f8751912560
child 68 8da9e0c16194
equal deleted inserted replaced
66:3506b681c191 67:ca5884c2e3bd
     1 // regular expressions
     1 // Scala Lecture 3
     2 // ?? polymorphism
     2 //=================
     3 
     3 
     4 A function should do one thing, and only one thing.
     4 
     5 
     5 // One of only two places where I conceded to mutable
     6 Make your variables immutable, unless there's a good reason not to.
     6 // data structures: The following function generates 
     7 
     7 // new labels
     8 You can be productive on Day 1, but the language is deep, so as you go
     8 
     9 along you’ll keep learning, and finding newer, better ways to write
     9 var counter = -1
    10 code. Scala will change the way you think about programming (and
    10 
    11 that’s a good thing).
    11 def fresh(x: String) = {
    12 
    12   counter += 1
    13 Of all of Scala’s benefits, what I like best is that it lets you write
    13   x ++ "_" ++ counter.toString()
    14 concise, readable code
    14 }
    15 
    15 
    16 
    16 fresh("x")
    17 
    17 fresh("x")
    18 ScalaTest download page: http://www.scalatest.org/download
    18 
       
    19 // this can be avoided, but would have made my code more
       
    20 // complicated
       
    21 
       
    22 
       
    23 // Tail recursion
       
    24 //================
       
    25 
       
    26 def my_contains(elem: Int, lst: List[Int]) : Boolean = lst match {
       
    27   case Nil => false
       
    28   case x::xs => 
       
    29     if (x == elem) true else my_contains(elem, xs)
       
    30 }
       
    31 
       
    32 my_contains(4, List(1,2,3))
       
    33 my_contains(2, List(1,2,3))
       
    34 
       
    35 my_contains(1000000, (1 to 1000000).toList)
       
    36 my_contains(1000001, (1 to 1000000).toList)
       
    37 
       
    38 
       
    39 //factorial 0.1
       
    40 
       
    41 def fact(n: Long): Long = 
       
    42   if (n == 0) 1 else n * fact(n - 1)
       
    43 
       
    44 fact(10000)
       
    45 
       
    46 
       
    47 def factT(n: BigInt, acc: BigInt): BigInt =
       
    48   if (n == 0) acc else factT(n - 1, n * acc)
       
    49 
       
    50 
       
    51 factT(10000, 1)
       
    52 
       
    53 // my_contains and factT are tail recursive 
       
    54 // you can check this with 
       
    55 
       
    56 import scala.annotation.tailrec
       
    57 
       
    58 // and the annotation @tailrec
       
    59 
       
    60 // for tail-recursive functions the compiler
       
    61 // generates a loop-like code, which does not
       
    62 // to allocate stack-space in each recursive
       
    63 // call; scala can do this only for tail-recursive
       
    64 // functions
       
    65 
       
    66 // consider the following "stupid" version of the
       
    67 // coin exchange problem: given some coins and a,
       
    68 // total, what is the change can you get
       
    69 
       
    70 val coins = List(4,5,6,8,10,13,19,20,21,24,38,39, 40)
       
    71 
       
    72 def first_positive[B](lst: List[Int], f: Int => Option[B]): Option[B] = lst match {
       
    73   case Nil => None
       
    74   case x::xs => 
       
    75     if (x <= 0) first_positive(xs, f)
       
    76     else {
       
    77       val fx = f(x)
       
    78       if (fx.isDefined) fx else first_positive(xs, f)
       
    79   }
       
    80 }
       
    81 
       
    82 
       
    83 def search(total: Int, coins: List[Int], cs: List[Int]): Option[List[Int]] = {
       
    84   if (total < cs.sum) None 
       
    85   else if (cs.sum == total) Some(cs) 
       
    86   else first_positive(coins, (c: Int) => search(total, coins, c::cs))
       
    87 }
       
    88 
       
    89 search(11, coins, Nil)
       
    90 search(111, coins, Nil)
       
    91 search(111111, coins, Nil)
       
    92 
       
    93 val junk_coins = List(4,-2,5,6,8,0,10,13,19,20,-3,21,24,38,39, 40)
       
    94 search(11, junk_coins, Nil)
       
    95 search(111, junk_coins, Nil)
       
    96 
       
    97 
       
    98 import scala.annotation.tailrec
       
    99 
       
   100 @tailrec
       
   101 def asearch(total: Int, coins: List[Int], acc_cs: List[List[Int]]): Option[List[Int]] = acc_cs match {
       
   102   case Nil => None
       
   103   case x::xs => 
       
   104     if (total < x.sum) asearch(total, coins, xs)
       
   105     else if (x.sum == total) Some(x) 
       
   106     else asearch(total, coins, coins.filter(_ > 0).map(_::x) ::: xs)
       
   107 }
       
   108 
       
   109 val start_acc = coins.filter(_ > 0).map(List(_))
       
   110 asearch(11, junk_coins, start_acc)
       
   111 asearch(111, junk_coins, start_acc)
       
   112 asearch(111111, junk_coins, start_acc)
       
   113 
       
   114 // moral: whenever a recursive function is resource-critical
       
   115 // (i.e. works on large recursion depth), then you need to
       
   116 // write it in tail-recursive fashion
       
   117 
       
   118 
       
   119 // Polymorphism
       
   120 //==============
       
   121 
       
   122 def length_int_list(lst: List[Int]): Int = lst match {
       
   123   case Nil => 0
       
   124   case x::xs => 1 + length_int_list(xs)
       
   125 }
       
   126 
       
   127 length_int_list(List(1, 2, 3, 4))
       
   128 
       
   129 
       
   130 def length[A](lst: List[A]): Int = lst match {
       
   131   case Nil => 0
       
   132   case x::xs => 1 + length(xs)
       
   133 }
       
   134 
       
   135 
       
   136 def map_int_list(lst: List[Int], f: Int => Int): List[Int] = lst match {
       
   137   case Nil => Nil
       
   138   case x::xs => f(x)::map_int_list(xs, f) 
       
   139 }
       
   140 
       
   141 map_int_list(List(1, 2, 3, 4), square)
       
   142 
       
   143 
       
   144 // Remember?
       
   145 def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ...
       
   146 
       
   147 
       
   148 // polymorphic classes
       
   149 //(trees with some content)
       
   150 
       
   151 abstract class Tree[+A]
       
   152 case class Node[A](elem: A, left: Tree[A], right: Tree[A]) extends Tree[A]
       
   153 case object Leaf extends Tree[Nothing]
       
   154 
       
   155 def insert[A](tr: Tree[A], n: A): Tree[A] = tr match {
       
   156   case Leaf => Node(n, Leaf, Leaf)
       
   157   case Node(m, left, right) => 
       
   158     if (n == m) Node(m, left, right) 
       
   159     else if (n < m) Node(m, insert(left, n), right)
       
   160     else Node(m, left, insert(right, n))
       
   161 }
       
   162 
       
   163 
       
   164 // the A-type needs to be ordered
       
   165 
       
   166 abstract class Tree[+A <% Ordered[A]]
       
   167 case class Node[A <% Ordered[A]](elem: A, left: Tree[A], right: Tree[A]) extends Tree[A]
       
   168 case object Leaf extends Tree[Nothing]
       
   169 
       
   170 
       
   171 def insert[A <% Ordered[A]](tr: Tree[A], n: A): Tree[A] = tr match {
       
   172   case Leaf => Node(n, Leaf, Leaf)
       
   173   case Node(m, left, right) => 
       
   174     if (n == m) Node(m, left, right) 
       
   175     else if (n < m) Node(m, insert(left, n), right)
       
   176     else Node(m, left, insert(right, n))
       
   177 }
       
   178 
       
   179 
       
   180 val t1 = Node(4, Node(2, Leaf, Leaf), Node(7, Leaf, Leaf))
       
   181 insert(t1, 3)
       
   182 
       
   183 val t2 = Node('b', Node('a', Leaf, Leaf), Node('f', Leaf, Leaf))
       
   184 insert(t2, 'e')
       
   185 
       
   186 
       
   187 
       
   188 // Regular expressions - the power of DSLs
       
   189 //=========================================
       
   190 
       
   191 
       
   192 abstract class Rexp
       
   193 case object ZERO extends Rexp
       
   194 case object ONE extends Rexp
       
   195 case class CHAR(c: Char) extends Rexp
       
   196 case class ALT(r1: Rexp, r2: Rexp) extends Rexp 
       
   197 case class SEQ(r1: Rexp, r2: Rexp) extends Rexp 
       
   198 case class STAR(r: Rexp) extends Rexp 
       
   199 
       
   200 
       
   201 // (ab)*
       
   202 val r0 = ??
       
   203 
       
   204 
       
   205 // some convenience for typing in regular expressions
       
   206 import scala.language.implicitConversions    
       
   207 import scala.language.reflectiveCalls 
       
   208 
       
   209 def charlist2rexp(s: List[Char]): Rexp = s match {
       
   210   case Nil => ONE
       
   211   case c::Nil => CHAR(c)
       
   212   case c::s => SEQ(CHAR(c), charlist2rexp(s))
       
   213 }
       
   214 implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList)
       
   215 
       
   216 
       
   217 val r1 = STAR("ab")
       
   218 val r2 = STAR("")
       
   219 val r3 = STAR(ALT("ab", "ba"))
       
   220 
       
   221 
       
   222 implicit def RexpOps (r: Rexp) = new {
       
   223   def | (s: Rexp) = ALT(r, s)
       
   224   def % = STAR(r)
       
   225   def ~ (s: Rexp) = SEQ(r, s)
       
   226 }
       
   227 
       
   228 implicit def stringOps (s: String) = new {
       
   229   def | (r: Rexp) = ALT(s, r)
       
   230   def | (r: String) = ALT(s, r)
       
   231   def % = STAR(s)
       
   232   def ~ (r: Rexp) = SEQ(s, r)
       
   233   def ~ (r: String) = SEQ(s, r)
       
   234 }
       
   235 
       
   236 val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
       
   237 val sign = "+" | "-" | ""
       
   238 val number = sign ~ digit ~ digit.% 
       
   239 
       
   240 
       
   241 
       
   242 // Lazyness with style
       
   243 //=====================
       
   244 
       
   245 // The concept of lazy evaluation doesn’t really exist in 
       
   246 // non-functional languages, but it is pretty easy to grasp. 
       
   247 // Consider first 
       
   248 
       
   249 def square(x: Int) = x * x
       
   250 
       
   251 square(42 + 8)
       
   252 
       
   253 // this is called strict evaluation
       
   254 
       
   255 
       
   256 def expensiveOperation(n: BigInt): Boolean = expensiveOperation(n + 1) 
       
   257 val a = "foo"
       
   258 val b = "foo"
       
   259 
       
   260 val test = if ((a == b) || expensiveOperation(0)) true else false
       
   261 
       
   262 // this is called lazy evaluation
       
   263 // you delay compuation until it is really 
       
   264 // needed; once calculated though, does not 
       
   265 // need to be re-calculated
       
   266 
       
   267 // a useful example is
       
   268 def time_needed[T](i: Int, code: => T) = {
       
   269   val start = System.nanoTime()
       
   270   for (j <- 1 to i) code
       
   271   val end = System.nanoTime()
       
   272   ((end - start) / i / 1.0e9) + " secs"
       
   273 }
       
   274 
       
   275 
       
   276 // streams (I do not care how many)
       
   277 // primes: 2, 3, 5, 7, 9, 11, 13 ....
       
   278 
       
   279 def generatePrimes (s: Stream[Int]): Stream[Int] =
       
   280   s.head #:: generatePrimes(s.tail filter (_ % s.head != 0))
       
   281 
       
   282 val primes: Stream[Int] = generatePrimes(Stream.from(2))
       
   283 
       
   284 primes.filter(_ > 100).take(2000).toList
       
   285 
       
   286 time_needed(1, primes.filter(_ > 100).take(2000).toList)
       
   287 time_needed(1, primes.filter(_ > 100).take(2000).toList)
       
   288 
       
   289 
       
   290 
       
   291 // streams are useful for implementing search problems ;o)
       
   292 
       
   293 
       
   294 
       
   295 
       
   296 // The End
       
   297 //=========
       
   298 
       
   299 // A function should do one thing, and only one thing.
       
   300 
       
   301 // Make your variables immutable, unless there's a good 
       
   302 // reason not to.
       
   303 
       
   304 // You can be productive on Day 1, but the language is deep.
       
   305 
       
   306 // I like best about Scala that it lets you write
       
   307 // concise, readable code