// Scala Lecture 1
//=================
// Value assignments
// (their names should be lower case)
//===================================
val x = 42
val y = 3 + 4
val z = x / y
// (you cannot reassign values: z = 9 will give an error)
// Hello World
//=============
// an example of a stand-alone scala file;
// in the coursework students must submit
// plain scala "work-sheets"
object Hello extends App {
println("hello world")
}
// can be called with
//
// $> scalac hello-world.scala
// $> scala Hello
//
// $> java -cp /usr/local/src/scala/lib/scala-library.jar:. Hello
// Collections
//=============
List(1,2,3,1)
Set(1,2,3,1)
1 to 10
(1 to 10).toList
(1 until 10).toList
// an element in a list
val lst = List(1, 2, 3, 1)
lst(0)
lst(2)
// some alterative syntax for lists
1::2::3::Nil
List(1, 2, 3) ::: List(4, 5, 6)
// Printing/Strings
//==================
println("test")
val tst = "This is a " + "test\n"
println(tst)
val lst = List(1,2,3,1)
println(lst.toString)
println(lst.mkString("\n"))
println(lst.mkString(", "))
// some methods take more than one argument
println(lst.mkString("[", ",", "]"))
// Conversion methods
//====================
List(1,2,3,1).toString
List(1,2,3,1).toSet
"hello".toList
1.toDouble
// useful list methods
List(1,2,3,4).length
List(1,2,3,4).reverse
List(1,2,3,4).max
List(1,2,3,4).min
List(1,2,3,4).sum
List(1,2,3,4).take(2).sum
List(1,2,3,4).drop(2).sum
List(1,2,3,4,3)indexOf(3)
"1,2,3,4,5".split(",").mkString("\n")
"1,2,3,4,5".split(",3,").mkString("\n")
// Types (slide)
//=======
/* Scala is a strongly typed language
* some base types
Int, Long, BigInt, Float, Double
String, Char
Boolean
* some compound types
List[Int],
Set[Double]
Pairs: (Int, String)
List[(BigInt, String)]
*/
// Smart Strings
//===============
println(">\n\n<")
println(""">\n<""")
println("""">\n<"""")
/* in Java
val lyrics = "Sun dips down, the day has gone. \n" +
"Witches, wolves and giants yawn. \n" +
"Queen and dragon, troll and gnome: \n" +
"tired buddies head for home"
*/
val lyrics = """Sun dips down, the day has gone.
|Witches, wolves and giants yawn.
|Queen and dragon, troll and gnome:
|tired buddies head for home""".stripMargin
println(lyrics)
// Pairs/Tuples
//==============
val p = (1, "one")
p._1
p._2
val t = (4,1,2,3)
t._4
// Function Definitions
//======================
def incr(x: Int) : Int = x + 1
def double(x: Int) : Int = x + x
def square(x: Int) : Int = x * x
square(6)
// 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 = {
// body
// }
// If-Conditionals
//=================
def fact(n: Int) : Int =
if (n == 0) 1 else n * fact(n - 1)
fact(5)
fact(150)
/* boolean operators
== equals
! not
&& || and, or
*/
def fact2(n: BigInt): BigInt =
if (n == 0) 1 else n * fact2(n - 1)
fact2(150)
def fib(n: Int): Int =
if (n == 0) 1 else
if (n == 1) 1 else fib(n - 1) + fib(n - 2)
//gcd - Euclid's algorithm
def gcd(a: Int, b: Int) : Int =
if (b == 0) a else gcd(b, a % b)
gcd(48, 18)
def power(x: Int, n: Int) : Int =
if (n == 0) 1 else x * power(x, n - 1)
power(5, 5)
// String Interpolations
//=======================
val n = 3
println("The square of " + n + " is " + square(n) + ".")
println(s"The square of ${n} is ${square(n)}.")
def gcd_db(a: Int, b: Int) : Int = {
println(s"Function called with ${a} and ${b}.")
if (b == 0) a else gcd_db(b, a % b)
}
gcd_db(48, 18)
// Asserts/Testing
//================
assert(gcd(48, 18) == 6)
assert(gcd(48, 18) == 5, "The gcd test failed")
// For-Comprehensions (not For-Loops)
//====================================
for (n <- (1 to 10).toList) yield square(n)
for (n <- (1 to 10).toList;
m <- (1 to 10).toList) yield m * n
val mult_table =
for (n <- (1 to 10).toList;
m <- (1 to 10).toList) yield m * n
mult_table.sliding(10,10).mkString("\n")
// with if-predicates
for (n <- (1 to 3).toList;
m <- (1 to 3).toList;
if (n + m) % 2 == 0) yield (n, m)
for (n <- (1 to 3).toList;
m <- (1 to 3).toList;
if ((n + m) % 2 == 0)) yield (n, m)
// with patterns
for ((m, n) <- List((1, 4), (2, 3), (3, 2), (4, 1))) yield m + n
for (p <- List((1, 4), (2, 3), (3, 2), (4, 1))) yield p._1 + p._2
// with only a side-effect (no list is produced),
// has no "yield"
for (n <- (1 to 10)) println(n)
// concurrency (ONLY WORKS IN SCALA 2.11.8, not in SCALA 2.12)
// (would need to have this wrapped into a function, or
// REPL called with scala -Yrepl-class-based)
for (n <- (1 to 10)) println(n)
for (n <- (1 to 10).par) println(n)
// for measuring time
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
}
val list = (1 to 1000000).toList
time_needed(10, for (n <- list) yield n + 42)
time_needed(10, for (n <- list.par) yield n + 42)
// mutable vs immutable factorial
def fact_mu(n: Long): Long = {
var result : Long = 1
var i = 1
while (i <= n) {
result = result * i
i = i + 1
}
result
}
def fact_im(num: Long): Long = {
if (num == 1) num else
num * fact_im(num - 1)
}
def test() = {
for (i <- (0 to 10).par) yield {
val l1 = for (n <- (List.fill(100000)(20 to 21)).flatten.par) yield fact_mu(n)
val l2 = for (n <- (List.fill(100000)(20 to 21)).flatten.par) yield fact_im(n)
l1.sum - l2.sum
}
}
test().sum
println(l1.sum - l2.sum)
// Webpages
//==========
import io.Source
// obtaining a webpage
val url = """https://nms.kcl.ac.uk/christian.urban/"""
Source.fromURL(url)("ISO-8859-1").mkString
// another example
//val url = """http://api.postcodes.io/postcodes/CR84LQ"""
// a function for looking up constituency data
def consty_lookup(pcode: String) : String = {
val url = "http://api.postcodes.io/postcodes/" + pcode
Source.fromURL(url).mkString.split(",")(16)
}
consty_lookup("CR84LQ")
consty_lookup("WC2B4BG")
val places =
List("CR84LQ", "WC2B4BG", "KY169QT", "CB11LY", "CB39AX")
for (s <- places) println(consty_lookup(s))
// A Web Crawler
//===============
//
// the idea is to look for dead links using the
// regular expression "https?://[^"]*"
import io.Source
import scala.util._
// gets the first 10K of a web-page
def get_page(url: String) : String = {
Try(Source.fromURL(url)("ISO-8859-1").take(10000).mkString).
getOrElse { println(s" Problem with: $url"); ""}
}
// regex for URLs
val http_pattern = """"https?://[^"]*"""".r
// drops the first and last character from a string
def unquote(s: String) = s.drop(1).dropRight(1)
def get_all_URLs(page: String): Set[String] =
http_pattern.findAllIn(page).map(unquote).toSet
// naive version of crawl - searches until a given depth,
// visits pages potentially more than once
def crawl(url: String, n: Int) : Unit = {
if (n == 0) ()
else {
println(s"Visiting: $n $url")
for (u <- get_all_URLs(get_page(url))) crawl(u, n - 1)
}
}
// some starting URLs for the crawler
val startURL = """https://nms.kcl.ac.uk/christian.urban/"""
//val startURL = """https://nms.kcl.ac.uk/luc.moreau/index.html"""
crawl(startURL, 2)
// Further Information
//=====================
// The Scala home page and general information is at
//
// http://www.scala-lang.org
// http://docs.scala-lang.org
//
//
// It should be fairly easy to install the Scala binary and
// run Scala on the commandline. There are also at least
// four IDEs you can use with Scala:
//
// (0) Some general information about setting up IDEs
// with Scala support can be found at
//
// http://docs.scala-lang.org/getting-started.html
//
//
// (1) Eclipse for Scala (one big bundle)
//
// http://scala-ide.org/download/sdk.html
//
// (2) IntelliJ (needs additional Plugins)
//
// https://www.jetbrains.com/idea/
// http://docs.scala-lang.org/getting-started-intellij-track/getting-started-with-scala-in-intellij.html
//
// (3) Sublime (not free, but unlimited trial period;
// needs Scala and SublimeREPL plugin)
//
// https://www.sublimetext.com
//
// (4) Emacs (old-fashioned, but reliable)
//
// https://www.gnu.org/software/emacs/
//
// I use the old scala-tool support for Emacs distributed at
//
// https://github.com/scala/scala-tool-support/tree/master/tool-support/emacs
//
// but there is also support for the newer Ensime Scala Mode
//
// http://ensime.org/editors/emacs/scala-mode/
//
// There is also Scala support in the Atom editor, but my
// experience is mixed. People also use Scala with Vim and Jedit.
// Finally there is an online editor specifically designed for
// running Scala applications (but do not blame mne if you lose all
// what you typed in):
//
// https://scalafiddle.io
//
//
//
// All of the IDEs above support a REPL for Scala. Some of them have
// the very nifty feature of a Scala Worksheet -- you just save your
// file and it will be automatically evaluated and the result pasted
// into your file. However, this way of writing Scala code never worked
// for me. I just use the REPL.
//
//
// Scala Library Docs
//====================
//
// http://www.scala-lang.org/api/current/
//
// Scala Tutorials
//
// http://docs.scala-lang.org/tutorials/
//
// There are also a massive number of Scala tutorials on youtube
// and there are tons of books and free material.
//
// advantage of case classes
//
case class Participant(name: String, score: Int, active: Boolean)
val ps = Seq(Participant("Jack", 34, true),
Participant("Tom", 51, true),
Participant("Bob", 90, false))
ps.filter(_.score < 50).
filter(_.active).
map(_.copy(active = false))
// another example why state is bad...does not work yet
// for measuring time
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
}
def santa_state(plan: List[Char]) : Int = {
var floor = 0
for (i <- plan.par) {
if (i == '(') {
floor += 1
} else {
floor -= 1
}
}
floor
}
def i(c: Char) = if (c == '(') 1 else -1
def santa_imutable(plan: List[Char]) : Int =
plan.par.map(i(_)).reduce(_ + _)
santa_state("(((((()))))".toList)
santa_imutable("(((((()))))".toList)
def randomString(length: Int) =
List.fill(length)((40 + scala.util.Random.nextInt(2)).toChar)
santa_state(randomString(100))
val large_string = randomString(3000000)
time_needed(10, santa_state(large_string))
time_needed(10, santa_imutable(large_string))