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