updated default tip
authorChristian Urban <christian.urban@kcl.ac.uk>
Thu, 27 Nov 2025 18:24:07 +0000
changeset 504 7653dd662db3
parent 503 e0ee3aa6334b
updated
handouts/pep-ho.pdf
handouts/pep-ho.tex
pics/cfl.png
pics/cfl2021.png
progs/lecture1.scala
progs/lecture2.scala
progs/mandelbrot.sc
slides/slides03.pdf
slides/slides03.tex
slides/thanks.png
Binary file handouts/pep-ho.pdf has changed
--- 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
Binary file pics/cfl.png has changed
Binary file pics/cfl2021.png has changed
--- 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
--- 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
 //================
--- 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)
Binary file slides/slides03.pdf has changed
--- 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}
Binary file slides/thanks.png has changed