// Scala Lecture 3+ −
//=================+ −
+ −
+ −
// One of only two places where I conceded to mutable+ −
// data structures: The following function generates + −
// new labels+ −
+ −
var counter = -1+ −
+ −
def fresh(x: String) = {+ −
counter += 1+ −
x ++ "_" ++ counter.toString()+ −
}+ −
+ −
fresh("x")+ −
fresh("x")+ −
+ −
// this can be avoided, but would have made my code more+ −
// complicated+ −
+ −
+ −
// Tail recursion+ −
//================+ −
+ −
def my_contains(elem: Int, lst: List[Int]) : Boolean = lst match {+ −
case Nil => false+ −
case x::xs => + −
if (x == elem) true else my_contains(elem, xs)+ −
}+ −
+ −
my_contains(4, List(1,2,3))+ −
my_contains(2, List(1,2,3))+ −
+ −
my_contains(1000000, (1 to 1000000).toList)+ −
my_contains(1000001, (1 to 1000000).toList)+ −
+ −
+ −
//factorial 0.1+ −
+ −
def fact(n: Long): Long = + −
if (n == 0) 1 else n * fact(n - 1)+ −
+ −
fact(10000)+ −
+ −
+ −
def factT(n: BigInt, acc: BigInt): BigInt =+ −
if (n == 0) acc else factT(n - 1, n * acc)+ −
+ −
+ −
factT(10000, 1)+ −
+ −
// my_contains and factT are tail recursive + −
// you can check this with + −
+ −
import scala.annotation.tailrec+ −
+ −
// and the annotation @tailrec+ −
+ −
// for tail-recursive functions the compiler+ −
// generates a loop-like code, which does not+ −
// to allocate stack-space in each recursive+ −
// call; scala can do this only for tail-recursive+ −
// functions+ −
+ −
// consider the following "stupid" version of the+ −
// coin exchange problem: given some coins and a,+ −
// total, what is the change can you get+ −
+ −
val coins = List(4,5,6,8,10,13,19,20,21,24,38,39, 40)+ −
+ −
def first_positive[B](lst: List[Int], f: Int => Option[B]): Option[B] = lst match {+ −
case Nil => None+ −
case x::xs => + −
if (x <= 0) first_positive(xs, f)+ −
else {+ −
val fx = f(x)+ −
if (fx.isDefined) fx else first_positive(xs, f)+ −
}+ −
}+ −
+ −
+ −
def search(total: Int, coins: List[Int], cs: List[Int]): Option[List[Int]] = {+ −
if (total < cs.sum) None + −
else if (cs.sum == total) Some(cs) + −
else first_positive(coins, (c: Int) => search(total, coins, c::cs))+ −
}+ −
+ −
search(11, coins, Nil)+ −
search(111, coins, Nil)+ −
search(111111, coins, Nil)+ −
+ −
val junk_coins = List(4,-2,5,6,8,0,10,13,19,20,-3,21,24,38,39, 40)+ −
search(11, junk_coins, Nil)+ −
search(111, junk_coins, Nil)+ −
+ −
+ −
import scala.annotation.tailrec+ −
+ −
@tailrec+ −
def asearch(total: Int, coins: List[Int], acc_cs: List[List[Int]]): Option[List[Int]] = acc_cs match {+ −
case Nil => None+ −
case x::xs => + −
if (total < x.sum) asearch(total, coins, xs)+ −
else if (x.sum == total) Some(x) + −
else asearch(total, coins, coins.filter(_ > 0).map(_::x) ::: xs)+ −
}+ −
+ −
val start_acc = coins.filter(_ > 0).map(List(_))+ −
asearch(11, junk_coins, start_acc)+ −
asearch(111, junk_coins, start_acc)+ −
asearch(111111, junk_coins, start_acc)+ −
+ −
// moral: whenever a recursive function is resource-critical+ −
// (i.e. works on large recursion depth), then you need to+ −
// write it in tail-recursive fashion+ −
+ −
+ −
// Polymorphism+ −
//==============+ −
+ −
def length_int_list(lst: List[Int]): Int = lst match {+ −
case Nil => 0+ −
case x::xs => 1 + length_int_list(xs)+ −
}+ −
+ −
length_int_list(List(1, 2, 3, 4))+ −
+ −
+ −
def length[A](lst: List[A]): Int = lst match {+ −
case Nil => 0+ −
case x::xs => 1 + length(xs)+ −
}+ −
+ −
+ −
def map_int_list(lst: List[Int], f: Int => Int): List[Int] = lst match {+ −
case Nil => Nil+ −
case x::xs => f(x)::map_int_list(xs, f) + −
}+ −
+ −
map_int_list(List(1, 2, 3, 4), square)+ −
+ −
+ −
// Remember?+ −
def first[A, B](xs: List[A], f: A => Option[B]): Option[B] = ...+ −
+ −
+ −
// polymorphic classes+ −
//(trees with some content)+ −
+ −
abstract class Tree[+A]+ −
case class Node[A](elem: A, left: Tree[A], right: Tree[A]) extends Tree[A]+ −
case object Leaf extends Tree[Nothing]+ −
+ −
def insert[A](tr: Tree[A], n: A): Tree[A] = tr match {+ −
case Leaf => Node(n, Leaf, Leaf)+ −
case Node(m, left, right) => + −
if (n == m) Node(m, left, right) + −
else if (n < m) Node(m, insert(left, n), right)+ −
else Node(m, left, insert(right, n))+ −
}+ −
+ −
+ −
// the A-type needs to be ordered+ −
+ −
abstract class Tree[+A <% Ordered[A]]+ −
case class Node[A <% Ordered[A]](elem: A, left: Tree[A], right: Tree[A]) extends Tree[A]+ −
case object Leaf extends Tree[Nothing]+ −
+ −
+ −
def insert[A <% Ordered[A]](tr: Tree[A], n: A): Tree[A] = tr match {+ −
case Leaf => Node(n, Leaf, Leaf)+ −
case Node(m, left, right) => + −
if (n == m) Node(m, left, right) + −
else if (n < m) Node(m, insert(left, n), right)+ −
else Node(m, left, insert(right, n))+ −
}+ −
+ −
+ −
val t1 = Node(4, Node(2, Leaf, Leaf), Node(7, Leaf, Leaf))+ −
insert(t1, 3)+ −
+ −
val t2 = Node('b', Node('a', Leaf, Leaf), Node('f', Leaf, Leaf))+ −
insert(t2, 'e')+ −
+ −
+ −
+ −
// Regular expressions - the power of DSLs+ −
//=========================================+ −
+ −
+ −
abstract class Rexp+ −
case object ZERO extends Rexp+ −
case object ONE extends Rexp+ −
case class CHAR(c: Char) extends Rexp+ −
case class ALT(r1: Rexp, r2: Rexp) extends Rexp + −
case class SEQ(r1: Rexp, r2: Rexp) extends Rexp + −
case class STAR(r: Rexp) extends Rexp + −
+ −
+ −
// (ab)*+ −
val r0 = ??+ −
+ −
+ −
// some convenience for typing in regular expressions+ −
import scala.language.implicitConversions + −
import scala.language.reflectiveCalls + −
+ −
def charlist2rexp(s: List[Char]): Rexp = s match {+ −
case Nil => ONE+ −
case c::Nil => CHAR(c)+ −
case c::s => SEQ(CHAR(c), charlist2rexp(s))+ −
}+ −
implicit def string2rexp(s: String): Rexp = charlist2rexp(s.toList)+ −
+ −
+ −
val r1 = STAR("ab")+ −
val r2 = STAR("")+ −
val r3 = STAR(ALT("ab", "ba"))+ −
+ −
+ −
implicit def RexpOps (r: Rexp) = new {+ −
def | (s: Rexp) = ALT(r, s)+ −
def % = STAR(r)+ −
def ~ (s: Rexp) = SEQ(r, s)+ −
}+ −
+ −
implicit def stringOps (s: String) = new {+ −
def | (r: Rexp) = ALT(s, r)+ −
def | (r: String) = ALT(s, r)+ −
def % = STAR(s)+ −
def ~ (r: Rexp) = SEQ(s, r)+ −
def ~ (r: String) = SEQ(s, r)+ −
}+ −
+ −
val digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"+ −
val sign = "+" | "-" | ""+ −
val number = sign ~ digit ~ digit.% + −
+ −
+ −
+ −
// Lazyness with style+ −
//=====================+ −
+ −
// The concept of lazy evaluation doesn’t really exist in + −
// non-functional languages, but it is pretty easy to grasp. + −
// Consider first + −
+ −
def square(x: Int) = x * x+ −
+ −
square(42 + 8)+ −
+ −
// this is called strict evaluation+ −
+ −
+ −
def expensiveOperation(n: BigInt): Boolean = expensiveOperation(n + 1) + −
val a = "foo"+ −
val b = "foo"+ −
+ −
val test = if ((a == b) || expensiveOperation(0)) true else false+ −
+ −
// this is called lazy evaluation+ −
// you delay compuation until it is really + −
// needed; once calculated though, does not + −
// need to be re-calculated+ −
+ −
// a useful example is+ −
def time_needed[T](i: Int, code: => T) = {+ −
val start = System.nanoTime()+ −
for (j <- 1 to i) code+ −
val end = System.nanoTime()+ −
((end - start) / i / 1.0e9) + " secs"+ −
}+ −
+ −
+ −
// streams (I do not care how many)+ −
// primes: 2, 3, 5, 7, 9, 11, 13 ....+ −
+ −
def generatePrimes (s: Stream[Int]): Stream[Int] =+ −
s.head #:: generatePrimes(s.tail filter (_ % s.head != 0))+ −
+ −
val primes: Stream[Int] = generatePrimes(Stream.from(2))+ −
+ −
primes.filter(_ > 100).take(2000).toList+ −
+ −
time_needed(1, primes.filter(_ > 100).take(2000).toList)+ −
time_needed(1, primes.filter(_ > 100).take(2000).toList)+ −
+ −
+ −
+ −
// streams are useful for implementing search problems ;o)+ −
+ −
+ −
+ −
+ −
// The End+ −
//=========+ −
+ −
// A function should do one thing, and only one thing.+ −
+ −
// Make your variables immutable, unless there's a good + −
// reason not to.+ −
+ −
// You can be productive on Day 1, but the language is deep.+ −
+ −
// I like best about Scala that it lets me write+ −
// concise, readable code+ −
+ −