# HG changeset patch # User Christian Urban # Date 1669046265 0 # Node ID f51e593903ac6c939864ef3f310451975db1bbfb # Parent 99dcfdf5aed814d38381416dc31ce7160027dcf2 updated diff -r 99dcfdf5aed8 -r f51e593903ac progs/lecture1.scala --- a/progs/lecture1.scala Thu Nov 17 20:05:36 2022 +0000 +++ b/progs/lecture1.scala Mon Nov 21 15:57:45 2022 +0000 @@ -1,7 +1,24 @@ // Scala Lecture 1 //================= +// - List, Sets, Strings, ... +// - Value assignments (val vs var) +// - How to define functions? (What is returned?) +// - If-Conditions +val tmp = 0 +val result = !(tmp == 0) +val result = if (tmp == 0) true else false + +// expressions (if (tmp == 0) true else false) +// - For-Comprehensions (guards, with/without yield) +// +// +// - Options +// - Higher-Order Functions (short-hand notation) +// - maps (behind for-comprehensions) +// - Pattern-Matching +// - String-Interpolations // Value assignments // (their names should be lower case) @@ -180,6 +197,7 @@ // you can make the type of a value explicit val name = "bob" +val name : String = "bob" // type errors math.sqrt("64".toDouble) @@ -327,8 +345,6 @@ // with if-predicates / filters -if (1 == 2) "a" else "b" - for (n <- (1 to 3).toList; m <- (1 to 3).toList; if (n + m) % 2 == 0) yield (n, m) @@ -453,7 +469,7 @@ def count_intersection(A: Set[Int], B: Set[Int]) : Int = { var count = 0 - for (x <- A; if (B contains x)) count += 1 + for (x <- A.par; if (B contains x)) count += 1 count } diff -r 99dcfdf5aed8 -r f51e593903ac progs/lecture2.scala --- a/progs/lecture2.scala Thu Nov 17 20:05:36 2022 +0000 +++ b/progs/lecture2.scala Mon Nov 21 15:57:45 2022 +0000 @@ -70,14 +70,19 @@ // List[Int]: Nil, List(_) // // Option[Int]: None, Some(0), Some(1), ... +// Option[Boolean]: None, Some(true), Some(false) // Option[...]: None, Some(_) def safe_div(x: Int, y: Int) : Option[Int] = if (y == 0) None else Some(x / y) -List(1,2,3,4,5,6).indexOf(7) +safe_div(10 + 5, 4 - 1) -def my_min(ls: List[Int]) : Option[Int] = ls.minOption +List(1,2,3,4,5,6).indexOf(7) +List[Int]().minOption + +def my_min(ls: List[Int]) : Option[Int] = + ls.minOption my_min(List(1,2,3,4)) @@ -90,9 +95,10 @@ import scala.util._ import io.Source -val my_url = "https://nms.kcl.ac.uk/christian.urban2/" +val my_url = "https://nms.kcl.ac.uk/christian.urban/" Source.fromURL(my_url)("ISO-8859-1").mkString +Source.fromURL(my_url)("ISO-8859-1").getLines().toList Try(Source.fromURL(my_url)("ISO-8859-1").mkString).getOrElse("") @@ -100,14 +106,18 @@ // the same for files + Try(Some(Source.fromFile("test.txt")("ISO-8859-1").mkString)).getOrElse(None) +Try(Source.fromFile("test.txt")("ISO-8859-1").mkString).toOption + +Using(Source.fromFile("test.txt")("ISO-8859-1"))(_.mkString).toOption // how to implement a function for reading -// (lines) something from files... +// (lines) from files... // def get_contents(name: String) : List[String] = - Source.fromFile(name)("ISO-8859-1").getLines.toList + Source.fromFile(name)("ISO-8859-1").getLines().toList get_contents("text.txt") get_contents("test.txt") @@ -120,7 +130,7 @@ // 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)("ISO-8859-1").getLines().toList)).getOrElse(None) get_contents("text.txt") get_contents("test.txt") @@ -158,8 +168,6 @@ val lst = List(None, Some(1), Some(2), None, Some(3)) - - // a function that turns strings into numbers // (similar to .toInt) Integer.parseInt("1234") @@ -234,7 +242,7 @@ lst.find(n => odd(n) && n > 2) - +// lexicographic ordering val ps = List((3, 0), (3, 2), (4, 2), (2, 2), (2, 0), (1, 1), (1, 0)) diff -r 99dcfdf5aed8 -r f51e593903ac wsheets/wsh01.scala --- a/wsheets/wsh01.scala Thu Nov 17 20:05:36 2022 +0000 +++ b/wsheets/wsh01.scala Mon Nov 21 15:57:45 2022 +0000 @@ -22,7 +22,7 @@ // Task 2 val z = 42 -z = z + 1 . // error +z = z + 1 // error val x = 2 * z val z = 466 // old z not accessible anymore println(x) diff -r 99dcfdf5aed8 -r f51e593903ac wsheets/wsh02.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wsheets/wsh02.scala Mon Nov 21 15:57:45 2022 +0000 @@ -0,0 +1,77 @@ + +// Task 1 + +List(7,2,3,4,5,6).find(_ < 4) // => Some(3) +List(5,6,7,8,9).find(_ < 4) // => None +List(5,6,7,8,9).min // => 5 +List(5,6,7,8,9).minOption // => Some(5) +List[Int]().minOption // => None + + +// Task 2 + +Try(Some(List(5,6,7,8,9).min)).getOrElse(None) +Try(Some(List[Int]().min)).getOrElse(None) + +// Task 3 +import scala.util._ +import io.Source + +val my_url = "https://nms.kcl.ac.uk/christian.urban/" + +// fails if there is no file with that name +Source.fromFile("test.txt")("ISO-8859-1").mkString +Source.fromFile("test.txt")("ISO-8859-1").getLines().toList + +// encapsulates the failure case as None +Try(Some(Source.fromFile("test.txt")("ISO-8859-1").mkString)).getOrElse(None) +Try(Source.fromFile("test.txt")("ISO-8859-1").mkString).toOption // same but shorter + +// for files with propper closing of the file after reading +Using(Source.fromFile("test.txt")("ISO-8859-1"))(_.mkString).toOption + +// Task 4 (Higher-Order Functions) + +List(7,2,3,4,5,6).find(_ < 4) +List(7,2,3,4,5,6).count(_ % 2 == 0) +List(7,2,3,4,5,6).sortWith(_ > _) +List(7,2,3,4,5,6).filter(_ > 4) + +// Task 5 (Maps) + +List(7,2,3,4,5,6).map(n => n * n) + +for (n <- List(7,2,3,4,5,6)) yield n * n + +// The advantages of for-comprehensions is that they +// can be nested and also can contain guards. In such +// cases the translations to maps and filters is a bit +// involved. + +// Task 6 (Pattern-Matching) + +def my_map(lst: List[Int], f: Int => Int) : List[Int] = { + if (lst == Nil) Nil + else f(lst.head) :: my_map(lst.tail, f) +} + +def my_map(lst: List[Int], f: Int => Int) : List[Int] = lst macth { + case Nil => Nil + case x::xs => f(x) :: my_map(xs, f) +} + +// Task 7 (Web-Crawler, hard) + +// see lecture2.scala + +// requires an accumulator that records all pages that have +// already been visited, for example + +def crawl(url: String, n: Int, acc : Set[String] = Set()) : Unit = { + if (n == 0) () + else { + println(s" Visiting: $n $url") + val urls = get_all_URLs(get_page(url)) + for (u <- urls) crawl(u, n - 1, acc | urls) + } +} diff -r 99dcfdf5aed8 -r f51e593903ac wsheets/wsh02.tex --- a/wsheets/wsh02.tex Thu Nov 17 20:05:36 2022 +0000 +++ b/wsheets/wsh02.tex Mon Nov 21 15:57:45 2022 +0000 @@ -31,76 +31,151 @@ \subsection*{Task 1 (Options)} -Get familiar with constructing strings and printing strings -(i.e.~\texttt{println}, \texttt{mkString}, \texttt{toString}, \ldots) +Get familiar with the return value of functions that can +``go wrong'': \begin{lstlisting}[numbers=none] scala> List(7,2,3,4,5,6).find(_ < 4) scala> List(5,6,7,8,9).find(_ < 4) -scala> +scala> List(5,6,7,8,9).min +scala> List(5,6,7,8,9).minOption +scala> List[Int]().minOption \end{lstlisting} -\subsection*{Task 2 (URLs / Files)} +\noindent +Note that there needs to be a type-annotation for \texttt{List()} otherwise +Scala will not know which \texttt{min}-version it should use. -ALso Try +\subsection*{Task 2 (Try)} + +The Scala-Idiom \texttt{Try-getOrElse} allows you to conveniently +deal with failure cases. \begin{lstlisting}[numbers=none] -scala> -scala> -scala> +scala> Try(Some(List(5,6,7,8,9).min)).getOrElse(None) +scala> Try(Some(List[Int]().min)).getOrElse(None) \end{lstlisting} -\subsection*{Task 3 (Higher-Order Functions)} +\noindent +Note that \texttt{Try} needs the library \texttt{scala.util.\_} to be +imported. -Make sure you understand if-conditions in Scala: They are -\emph{expressions}, meaning they need to calculate a result. For -example an \texttt{if} without an else-branch is nearly always -\emph{not} what you intend to write (because you are not meant to -change any ``state'' outside the expression). Also, remember the quirks -in Scala with if-conditions needing parentheses and there is no -\texttt{then}-keyword. \begin{lstlisting}[numbers=none] -scala> val s1 = "foo" -scala> val s2 = "bar" -scala val s3 = "foobar" -scala> if (s1 == s2) print("equal") else print("unequal") -scala> if (s1 ++ s2 == s3) print("equal") else print("unequal") +def safe_div(x: Int, y: Int) : Option[Int] = + Try(Some(x / y)).getOrElse(None) +\end{lstlisting} + +\subsection*{Task 3 (URLs / Files)} + +For simple tasks such as reading webpages and files, Scala provides +convenient functions \texttt{Source.fromURL} and \texttt{Source.fromFile}. +To try them out, you need to import \texttt{io.Source}. + +\begin{lstlisting}[numbers=none] +scala> Source.fromURL(my_url)("ISO-8859-1").mkString +scala> Source.fromFile(my_file)("ISO-8859-1").mkString +\end{lstlisting} + +\noindent +These functions return an iterator, which can be transformed into a String +using \texttt{mkString}. The second argument fixes the character encoding +and should not be omitted. If you are interested in the individual lines +in the file, for example, you can use + +\begin{lstlisting}[numbers=none] +Source.fromFile(my_file)("ISO-8859-1") + .getLines().toList \end{lstlisting} -\subsection*{Task 4 (Maps)} +\noindent +If you are after proper error-handling, then you can use Scala's options +as follows + +\begin{lstlisting}[numbers=none] +Try(Some(Source.fromFile("test.txt")("ISO-8859-1") + .mkString)).getOrElse(None) +\end{lstlisting} + +This can also be written slightly shorter as -Write \texttt{for}-comprehensions that enumerate all triples containing the numbers 1 - 5. That is, -print the list +\begin{lstlisting}[numbers=none] +Try(Source.fromFile("test.txt")("ISO-8859-1") + .mkString).toOption +\end{lstlisting} -\[ -\texttt{(1,1,1)}, \texttt{(2,1,1)}, \texttt{(3,1,1)} \;\ldots\; \texttt{(5,5,5)} -\] +\noindent +In case of reading files, there can be an issue with closing +files properly. For this Scala provides \texttt{Using} + +\begin{lstlisting}[numbers=none] + Using(Source.fromFile("test.txt")("ISO-8859-1")) + (_.mkString).toOption +\end{lstlisting} \noindent -Modify the \texttt{for}-comprehensions such that only triples are -printed where the sum is divisible by 3. Then sort the list according -to the middle element (for this use \texttt{sortBy}). - -\subsection*{Task 5 (Pattern-Matching)} +This closes the files automatically after reading, but otherwise +behaves as the code shown above: It gives a \texttt{Some} in the +success case and \texttt{None} in the failure case. However, +\texttt{Using} requires a function as argument for prescribing +of what to do with the file content in the success case. -\subsection*{Task 6 (Web-Crawler)} +\subsection*{Task 4 (Higher-Order Functions)} -Write a pretty print function for lists of integers which -``condenses'' elements in the list, meaning if there is a number -several times in a row, then print out \mbox{\texttt{n x e}}, where -\texttt{n} is the number of repetitions and \texttt{e} is the -number. For example +Higher-Order functions means that Scala allows functions to +have functions as arguments and also allows functions to +return functions. Get familiar with the short-hand notation +for simple functions \begin{lstlisting}[numbers=none] - List(1,1,1,2,3,3) => List(3 x 1, 2, 2 x 3) - List(1,2,3,4,4,4) => List(1, 2, 3, 3 x 4) - List(1,1,1,1,1,1) => List(6 x 1) - List(1,1,1,2,1,1) => List(3 x 1, 2, 2 x 1) +scala> List(7,2,3,4,5,6).find(_ < 4) +scala> List(7,2,3,4,5,6).count(_ % 2 == 0) +scala> List(7,2,3,4,5,6).sortWith(_ > _) +scala> List(7,2,3,4,5,6).filter(_ > 4) \end{lstlisting} -You might like to define a separate function that first counts the occurences -for each element and returns a list of (Int, Int)-pairs . +\noindent +Be aware that this short-hand notation only works for ``smallish'' functions +and that sometimes Scala cannot figure out the types involved without +explicit type annotations. + +\subsection*{Task 5 (Maps)} + +Get familiar with the map-function for lists, sets etc. It is the +quintessential higher-order function and frequently used for transforming +lists. + +\begin{lstlisting}[numbers=none] +scala> List(7,2,3,4,5,6).map(n => n * n) +\end{lstlisting} + +\noindent +Make also sure you see that Scala's \texttt{for}-comprehensions +are just syntactic sugar for \texttt{map}s. What would this +expression look like as \texttt{for}-comprehension? What are +the advantages of \texttt{for}-comprehensions over \texttt{map}s. + + +\subsection*{Task 6 (Pattern-Matching)} + +Rewrite the following function using pattern-matching + +\begin{lstlisting}[numbers=none] +def my_map(lst: List[Int], f: Int => Int) : List[Int] = { + if (lst == Nil) Nil + else f(lst.head) :: my_map(lst.tail, f) +} +\end{lstlisting} + +\noindent +Observe that the type of the function is from \texttt{Int}s to +\texttt{Int}s, which is written in Scala as type \texttt{Int => Int}. + + +\subsection*{Task 7 (Web-Crawler, Hard)} + +Have a look at the web-crawler at the end of \texttt{lecture2.scala}. +Can you modify it such that every page is only visited once? \end{document}