| author | Christian Urban <urbanc@in.tum.de> | 
| Tue, 21 Nov 2017 16:31:11 +0000 | |
| changeset 152 | 16dbc95d7d77 | 
| parent 77 | 3cbe3d90b77f | 
| child 153 | 316f9c6cc2ff | 
| 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  |