# HG changeset patch # User Christian Urban # Date 1764267847 0 # Node ID 7653dd662db340fed596d998f55ed615fb771727 # Parent e0ee3aa6334ba5ad4c274a4ed5379d7c3def9a3f updated diff -r e0ee3aa6334b -r 7653dd662db3 handouts/pep-ho.pdf Binary file handouts/pep-ho.pdf has changed diff -r e0ee3aa6334b -r 7653dd662db3 handouts/pep-ho.tex --- a/handouts/pep-ho.tex Sun Nov 16 12:26:31 2025 +0000 +++ b/handouts/pep-ho.tex Thu Nov 27 18:24:07 2025 +0000 @@ -154,7 +154,8 @@ \subsection*{Introduction} \noindent -Scala is a programming language that combines functional and +I am happy to show you the Scala programming language during the next +five weeks. Scala is a programming language that combines functional and object-oriented programming-styles. It has received quite a bit of attention in the last ten or so years. One reason for this attention is that, like the Java programming language, Scala compiles to the Java diff -r e0ee3aa6334b -r 7653dd662db3 pics/cfl.png Binary file pics/cfl.png has changed diff -r e0ee3aa6334b -r 7653dd662db3 pics/cfl2021.png Binary file pics/cfl2021.png has changed diff -r e0ee3aa6334b -r 7653dd662db3 progs/lecture1.scala --- a/progs/lecture1.scala Sun Nov 16 12:26:31 2025 +0000 +++ b/progs/lecture1.scala Thu Nov 27 18:24:07 2025 +0000 @@ -1,6 +1,7 @@ // Scala Lecture 1 //================= +print("Hello") // Topics //-------- @@ -69,7 +70,7 @@ // ranges 1 to 10 -(1 to 10).toList +(1 to 100).toString (1 to 10).toList.toString @@ -183,7 +184,7 @@ val name : Int = "bob" // type errors -math.sqrt("64") +math.sqrt(64) // produces // @@ -217,18 +218,22 @@ // .... // } +def incr(x: Int) : Int = x + 1 +incr(42) + +def add(x: Int, y: Int) : Int = x + y + + def average(ls: List[Int]) : Int = { - println(s"$ls") val s = ls.sum val l = ls.length - if (l == 0) 0 - else s / l + s / l } -average(List(1,2,3,4,56)) +average(List(1,2,3,4)) -def incr(x: Int) : Int = x + 1 + def double(x: Int) : Int = x + x def square(x: Int) : Int = x * x @@ -320,20 +325,25 @@ // For-Comprehensions (not For-Loops) //==================================== -val lst = (1 to 10).toSet -val result = for (n <- lst) yield { +val lst = (1 to 10).toList +for (n <- lst) yield { n * n } println(result) +def square(n: Int) = n * n +def double(n: Int) = n + n + for (n <- lst) yield { square(n) + double(n) } -for (n <- (1 to 10).toList; - m <- (1 to 5).toList) yield (n, m) +val foo = for (n <- (1 to 10).toList; + m <- (1 to 5).toList; + if (n + m) % 2 == 0) yield (n, m) +foo.head // you can assign the result of a for-comprehension // to a value @@ -376,10 +386,12 @@ // general pattern of for-yield // (yield can be several lines) +/* for (pat <- ...) yield { // potentially complicated // calculation of a result } +*/ // For without yield //=================== @@ -387,6 +399,8 @@ // with only a side-effect (no list is produced), // has no "yield" +for (n <- (1 to 10).toList) yield println(n * n) + val xs = for (n <- (1 to 10).toList) println(n * n) xs.tail // error @@ -478,10 +492,12 @@ // But what the heck....lets try to count to 1 Mio in parallel // // requires -// scala --extra-jars scala- parallel-collections_3-1.0.4.jar +// scala --extra-jars scala-parallel-collections_3-1.2.0.jar import scala.collection.parallel.CollectionConverters._ +for (n <- (1 to 10).par) println(n) + def test() = { var cnt = 0 @@ -552,8 +568,8 @@ // you can also implement your own string interpolations extension (sc: StringContext) { - def i(args: Any*): String = s"\t${sc.s(args:_*)}\n" - def l(args: Any*): String = s"${sc.s(args:_*)}:\n" + def i(args: Any*): String = s"\t${sc.s(args*)}\n" + def l(args: Any*): String = s"${sc.s(args*)}:\n" } // this allows you to write things like diff -r e0ee3aa6334b -r 7653dd662db3 progs/lecture2.scala --- a/progs/lecture2.scala Sun Nov 16 12:26:31 2025 +0000 +++ b/progs/lecture2.scala Thu Nov 27 18:24:07 2025 +0000 @@ -7,6 +7,35 @@ // - Pattern-Matching // - Recursion + +// Function Definitions +//====================== + +// The general scheme for a function: you have to give a +// type to each argument and a return type of the function +// +// def fname(arg1: ty1, arg2: ty2,..., argn: tyn): rty = { +// .... +// } + +def incr(x: Int) : Int = x + 1 +incr(42) + +def add(x: Int, y: Int) : Int = x + y + + +def average(ls: List[Int]) : Option[Int] = { + val s = ls.sum + val l = ls.length + if (l == 0) None else Some(s / l) +} + +average(List(1,2,3,4)) +average(List()) + + + + // The Option Type //================= @@ -45,8 +74,8 @@ safe_div(10 + 5, 3) List(1,2,3,4,5,6).indexOf(7) -List[Int](3,4,5).min -List[Int]().minOption +List[Int]().min +List[Int](1,2,3).minOption @@ -58,42 +87,45 @@ import scala.util._ // Try,... import io.Source // fromURL -val my_url = "https://nms.kcl.ac.uk/christian.urban/" +//val my_url = "https://nms.kcl.ac.uk/christian.urban/" +val my_url = "https://urbanchr.github.io/" + +println(Try(Source.fromURL(my_url)(using "ISO-8859-1").mkString).toOption) -Source.fromURL(my_url)("ISO-8859-1").mkString -Source.fromURL(my_url)("ISO-8859-1").getLines().toList +.mkString +Source.fromURL(my_url)(using "ISO-8859-1").getLines().toList -Try(Source.fromURL(my_url)("ISO-8859-1").mkString).getOrElse("") +Try(Source.fromURL(my_url)(using "ISO-8859-1").mkString).getOrElse("") -Try(Some(Source.fromURL(my_url)("ISO-8859-1").mkString)).getOrElse(None) +Try(Some(Source.fromURL(my_url)(using "ISO-8859-1").mkString)).getOrElse(None) // the same for files -Try(Some(Source.fromFile("test.txt")("ISO-8859-1").mkString)).getOrElse(None) +Try(Some(Source.fromFile("test.txt")(using "ISO-8859-1").mkString)).getOrElse(None) -Try(Source.fromFile("test.txt")("ISO-8859-1").mkString).toOption +Try(Source.fromFile("test.txt")(using "ISO-8859-1").mkString).toOption -Using(Source.fromFile("test.txt")("ISO-8859-1"))(_.mkString).toOption +Using(Source.fromFile("test.txt")(using "ISO-8859-1"))(_.mkString).toOption // how to implement a function for reading // (lines) from files... // def get_contents(name: String) : List[String] = - Source.fromFile(name)("ISO-8859-1").getLines().toList + Source.fromFile(name)(using "ISO-8859-1").getLines().toList get_contents("text.txt") get_contents("test.txt") // slightly better - return Nil def get_contents(name: String) : List[String] = - Try(Source.fromFile(name)("ISO-8859-1").getLines.toList).getOrElse(List()) + Try(Source.fromFile(name)(using "ISO-8859-1").getLines.toList).getOrElse(List()) get_contents("text.txt") // much better - you record in the type that things can go wrong def get_contents(name: String) : Option[List[String]] = - Try(Some(Source.fromFile(name)("ISO-8859-1").getLines().toList)).getOrElse(None) + Try(Some(Source.fromFile(name)(using "ISO-8859-1").getLines().toList)).getOrElse(None) get_contents("text.txt") get_contents("test.txt") @@ -175,20 +207,55 @@ // minByOption // maxByOption +def altproduct(xs: List[Int]) : List[Int] = xs match { + case Nil => Nil + case (x::y::xs) => x * y :: altproduct(y::xs) + case (x::Nil) => List(x) +} + +altproduct(List(1,2,3,4,5)) + +def powerset(xs: Set[Int]) : Set[Set[Int]] = { + if (xs == Set()) Set(Set()) + else { + powerset(xs.tail) ++ powerset(xs.tail).map(_ + xs.head) + } +} + +powerset(Set(1,2,3)).mkString("\n") + // Higher-Order Functions //======================== // functions can take functions as arguments // and produce functions as result +val foo = for (n <- (1 to 10).toList) yield n * n + + def even(x: Int) : Boolean = x % 2 == 0 + +even(2) +even(3) + +def even(x: Int) : Boolean = x % 2 == 1 + +val foo_fun = even + +foo_fun(2) +foo_fun(3) + + def odd(x: Int) : Boolean = x % 2 == 1 def inc(x: Int) : Int = x + 1 val lst = (1 to 10).toList lst.filter(odd) -lst.exists(even) +lst.find(even) +lst.find(_ % 2 == 0) +lst.sortWith(_ < _) // (x,y) => x < y +lst.find(_ > 4) lst.count(odd) lst.filter(_ % 2 == 0) @@ -466,13 +533,59 @@ - + +// Recursion +//=========== + +// my_length + +def my_length(xs: List[Int]) : Int = { + if (xs == Nil) 0 else 1 + my_length(xs.tail) +} + +def my_sum(xs: List[Int]) : Int = { + if (xs == Nil) 0 else xs.head + my_sum(xs.tail) +} + +my_sum((1 to 100).toList) +my_length(List()) + +/* my_map */ +for (n <- List[Int](1,2,3)) yield n * n + +List(1,2,3) ::: List(3,4,5) +3 :: List(3,4,5) + +def my_map(xs: List[Int], f : Int => Int ) : List[Int] = { + if (xs == Nil) Nil + else f(xs.head) :: my_map(xs.tail, f) +} + +def square(n: Int) : Int = n * n + + +my_map(List(1,2,3), square) -// Recursion -//=========== +/* powerset */ + +def powerset(xs: Set[Int]) : Set[Set[Int]] = { + if (xs == Set()) Set(Set()) + else powerset(xs.tail) ++ powerset(xs.tail).map(_ + xs.head) +} + + + + + +/* on lists */ +def powerset(xs: List[Int]) : List[List[Int]] = { + if (xs == Nil) List(Nil) + else powerset(xs.tail) ::: powerset(xs.tail).map(xs.head :: _) +} + /* Say you have characters a, b, c. @@ -525,7 +638,7 @@ // gets the first 10K of a web-page def get_page(url: String) : String = { - Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString). + Try(Source.fromURL(url)(using "ISO-8859-1").take(10000).mkString). getOrElse { println(s" Problem with: $url"); ""} } @@ -555,7 +668,9 @@ } // some starting URLs for the crawler -val startURL = """https://nms.kcl.ac.uk/christian.urban/""" +//val startURL = """https://nms.kcl.ac.uk/christian.urban/""" +val startURL = """https://urbanchr.github.io/""" + crawl(startURL, 2) @@ -582,7 +697,166 @@ +def powerset(xs: Set[Int]) : Set[Set[Int]] = { + if (xs == Set()) Set(Set()) + else { + val ps = powerset(xs.tail) + ps ++ ps.map(_ + xs.head) + } +} +def psubsets(xs: Set[Int]) = + powerset(xs) -- Set(Set(), xs) + +//def psubsets(xs: Set[Int]) = +// xs.subsets.toList -- Set(Set(), xs) + +def splits(xs: Set[Int]) : Set[(Set[Int], Set[Int])] = + psubsets(xs).map(s => (s, xs -- s)) + + + +enum Tree { + case Num(i: Int) + case Add(l: Tree, r: Tree) + case Sub(l: Tree, r: Tree) + case Mul(l: Tree, r: Tree) + case Div(l: Tree, r: Tree) +} +import Tree._ + +def pp(tr: Tree) : String = tr match { + case Num(n) => s"$n" + case Add(l, r) => s"(${pp(l)} + ${pp(r)})" + case Sub(l, r) => s"(${pp(l)} - ${pp(r)})" + case Mul(l, r) => s"(${pp(l)} * ${pp(r)})" + case Div(l, r) => s"(${pp(l)} / ${pp(r)})" +} + +def search(nums: Set[Int]) : Set[Tree] = nums.size match { + case 0 => Set() + case 1 => Set(Num(nums.head)) + case 2 => { + val l = nums.head + val r = nums.tail.head + Set(Add(Num(l), Num(r)), + Mul(Num(l), Num(r))) + ++ Option.when(l <= r)(Sub(Num(r), Num(l))) + ++ Option.when(l > r)(Sub(Num(l), Num(r))) + ++ Option.when(r > 0 && l % r == 0)(Div(Num(l), Num(r))) + ++ Option.when(l > 0 && r % l == 0)(Div(Num(r), Num(l))) + } + case xs => { + val spls = splits(nums) + val subtrs = + for ((lspls, rspls) <- spls; + lt <- search(lspls); + rt <- search(rspls)) yield { + Set(Add(lt, rt), Sub(lt, rt), + Mul(lt, rt), Div(lt, rt)) + } + subtrs.flatten + } +} + +println(search(Set(1,2,3,4)).mkString("\n")) + +def eval(tr: Tree) : Option[Int] = tr match { + case Num(n) => Some(n) + case Add(l, r) => + for (rl <- eval(l); rr <- eval(r)) yield rl + rr + case Mul(l, r) => + for (rl <- eval(l); rr <- eval(r)) yield rl * rr + case Sub(l, r) => + for (rl <- eval(l); rr <- eval(r); + if 0 <= rl - rr) yield rl - rr + case Div(l, r) => + for (rl <- eval(l); rr <- eval(r); + if rr > 0 && rl % rr == 0) yield rl / rr +} + +eval(Add(Num(1), Num(2))) +eval(Mul(Add(Num(1), Num(2)), Num(4))) +eval(Sub(Num(3), Num(2))) +eval(Sub(Num(3), Num(6))) +eval(Div(Num(6), Num(2))) +eval(Div(Num(6), Num(4))) + +def time_needed[T](n: Int, code: => T) = { + val start = System.nanoTime() + for (i <- (0 to n)) code + val end = System.nanoTime() + (end - start) / 1.0e9 +} + + +import scala.collection.parallel.CollectionConverters._ + +def check(xs: Set[Int], target: Int) = + search(xs).find(eval(_) == Some(target)) + +for (sol <- check(Set(50, 5, 4, 9, 10, 8), 560)) { + println(s"${pp(sol)} => ${eval(sol)}") +} + + + +time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560)) + + +println(check(Set(25, 5, 2, 10, 7, 1), 986).mkString("\n")) + +for (sol <- check(Set(25, 5, 2, 10, 7, 1), 986)) { + println(s"${pp(sol)} => ${eval(sol)}") +} + +for (sol <- check(Set(25, 5, 2, 10, 7, 1), -1)) { + println(s"${pp(sol)} => ${eval(sol)}") +} + +for (sol <- check(Set(100, 25, 75, 50, 7, 10), 360)) { + println(s"${pp(sol)} => ${eval(sol)}") +} +time_needed(1, check(Set(100, 25, 75, 50, 7, 10), 360)) + + + +time_needed(1, check(Set(25, 5, 2, 10, 7, 1), 986)) +time_needed(1, check(Set(25, 5, 2, 10, 7, 1), -1)) + + +def generate(nums: Set[Int]) : Set[(Tree, Int)] = nums.size match { + case 0 => Set() + case 1 => Set((Num(nums.head), nums.head)) + case xs => { + val spls = splits(nums) + val subtrs = + for ((lspls, rspls) <- spls; + (lt, ln) <- generate(lspls); + (rt, rn) <- generate(rspls)) yield { + Set((Add(lt, rt), ln + rn), + (Mul(lt, rt), ln * rn)) + ++ Option.when(ln <= rn)((Sub(rt, lt), rn - ln)) + ++ Option.when(ln > rn)((Sub(lt, rt), ln - rn)) + ++ Option.when(rn > 0 && ln % rn == 0)((Div(lt, rt), ln / rn)) + ++ Option.when(ln > 0 && rn % ln == 0)((Div(rt, lt), rn / ln)) + } + subtrs.flatten + } +} + +def check2(xs: Set[Int], target: Int) = + generate(xs).find(_._2 == target) + +for ((sol, ev) <- check2(Set(50, 5, 4, 9, 10, 8), 560)) { + println(s"${pp(sol)} => ${eval(sol)} / $ev") +} + +time_needed(1, check(Set(50, 5, 4, 9, 10, 8), 560)) +time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), 560)) + +time_needed(1, check(Set(50, 5, 4, 9, 10, 8), -1)) +time_needed(1, check2(Set(50, 5, 4, 9, 10, 8), -1)) // Jumping Towers //================ diff -r e0ee3aa6334b -r 7653dd662db3 progs/mandelbrot.sc --- a/progs/mandelbrot.sc Sun Nov 16 12:26:31 2025 +0000 +++ b/progs/mandelbrot.sc Thu Nov 27 18:24:07 2025 +0000 @@ -122,8 +122,8 @@ val d_x = (end.re - start.re) / W val d_y = (end.im - start.im) / H - for (y <- (0 until H)) { - for (x <- (0 until W)) { + for (y <- (0 until H).par) { + for (x <- (0 until W).par) { val c = start + x * d_x + y * d_y * i val iters = iterations(c, max) diff -r e0ee3aa6334b -r 7653dd662db3 slides/slides03.pdf Binary file slides/slides03.pdf has changed diff -r e0ee3aa6334b -r 7653dd662db3 slides/slides03.tex --- a/slides/slides03.tex Sun Nov 16 12:26:31 2025 +0000 +++ b/slides/slides03.tex Thu Nov 27 18:24:07 2025 +0000 @@ -64,10 +64,10 @@ %Office: & N\liningnums{7.07} (North Wing, Bush House)\bigskip\\ Slides \& Code: & KEATS\bigskip\\ - Office Hour: & Thursdays 13:00 -- 14:00 (send email first)\\ + Office Hour: & Fridays 11:30 -- 12:30 (send email first)\\ Location: & N7.07 (North Wing, Bush House)\bigskip\\ - Pollev: & \texttt{\alert{https://pollev.com/cfltutoratki576}}\\ \\ + %Pollev: & \texttt{\alert{https://pollev.com/cfltutoratki576}}\\ \\ %Additionally: & (for Scala) Tuesdays 10:45 -- 11:45\\ \end{tabular} \end{center} @@ -404,6 +404,8 @@ \end{frame} +\end{document} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% { \setbeamercolor{background canvas}{bg=cream} diff -r e0ee3aa6334b -r 7653dd662db3 slides/thanks.png Binary file slides/thanks.png has changed