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