1. Scala syntax, function definition and higher-order functions

Objectives:

  • get yourself familiar with Scala syntax basics
  • practice writing tail-recursive functions as an alternative to imperative loops
  • keep your code clean and well-structured.

Make sure you have done your scala environment setup ready

Create a new Scala worksheet to write your solutions

1.1. Write a tail-recursive function that computes the factorial of a natural number. Start from the code stub below:

def fact (n: Int): Int = {
   def aux_fact(n: Int, acc: Int): Int = 
       if (???) acc
       else ???
   ???
}

1.2. Implement a tail-recursive function that computes the greatest common divisor of a natural number:

def gcd(a: Int, b: Int): Int = ???

1.3. Write a tail-recursive function takes an integer $ n$ and computes the value $ 1 + 2^2 + 3^2 + \ldots + (n-1)^2 + n^2$ . (Hint: use inner functions).

def sumSquares(n: Int): Int = ???

Newton's Square Root method

A very fast way to numerically compute $ \sqrt{a}$ , often used as a standard sqrt(.) implementation, relies on Newton's Square Root approximation. The main idea relies on starting with an estimate (often 1), and incrementally improving the estimate. More precisely:

  • Start with $ x_0 = 1$ .
  • Compute $ x_{n+1} = \displaystyle\frac{1}{2}(x_n+\frac{a}{x_n})$

1.4. Implement the function improve which takes an estimate $ x_n$ of $ \sqrt{a}$ and improves it (computes $ x_{n+1}$ ).

def improve(xn: Double, a: Double): Double = ???

1.5. Implement the function nthGuess which starts with $ x_0 = 1$ and computes the nth estimate $ x_n$ of $ \sqrt{a}$ :

def nth_guess(n: Int, a: Double): Double = ???

Note that:

  • for smaller $ a$ , there is no need to compute $ n$ estimations as $ (x_n)_n$ converges quite fast to $ \sqrt{a}$ .

1.6. Thus, implement the function acceptable which returns true iff $ \mid x_n^2 - a \mid \leq 0.001$ . (Hint, google the abs function in Scala. Don't forget to import scala.math._).

  def acceptable(xn: Double, a: Double): Boolean = ???

1.7. Implement the function mySqrt which computes the square root of an integer a. Modify the previous implementations to fit the following code structure:

def mySqrt(a: Double): Double = {
   def improve(xn: Double): Double = ???
   def acceptable(xn: Double): Boolean = ???
 
   def tailSqrt(estimate: Double): Double = ???
 
   ???
}

1.8. (!) Try out your code for: 2.0e50 (which is $ 2.0\cdot 10^{50}$ ) or 2.0e-50. The code will likely take a very long time to finish. The reason is that $ xn^2 - a$ will suffer from rounding error which may be larger than 0.001. Can you find a different implementation for the function acceptable which takes that into account? (Hint: the code is just as simple as the original one).

Objectives:

  • implement and use higher-order functions. A higher-order function takes other functions as parameter or returns them
  • implement curry and uncurry functions, and how they should be properly used (review lecture).

2.1. Define the function foldWith which uses an operation op to reduce a range of integers to a value. For instance, given that op is addition (+), the result of folding the range 1 to 3 will be 1+2+3=6. foldWith should be curried (it will take the operation and return another function which expects the bounds).

def foldWith (op: (Int,Int) => Int)(start: Int, stop: Int): Int = {
  def tail_fold(crt: Int, acc: Int): Int  = ???
  ??
}

2.2. Define the function foldConditional which extends foldWith by also adding a predicate p: Int ⇒ Int. foldConditional will reduce only those elements of a range which satisfy the predicate.

def foldConditional(op: (Int,Int) => Int, p: Int => Boolean)(start: Int, stop: Int): Int = ???

2.3. [should be revised] Let $ count_k(n) = k + 2k + 3k + \ldots x*k$ , with $ x*k \leq n$ be the sum of all multiples of $ k$ within the range 1,n. Write a function alldivs which computes the sum: $ count_1(n) + count_2(n) + \ldots + count_k(n)$ . (Hint, use foldConditional).

def alldivs(n: Int): Int = ???

2.4. Write a function foldMap which takes values $ a_1, a_2, \ldots, a_k$ from a range and computes $ f(a_1)\;op\;f(a_2)\;op\;\ldots f(a_k)$ .

def foldMap(op: (Int,Int) => Int, f: Int => Int)(start: Int, stop: Int): Int = ???

2.5. Write a function which computes $ 1 + 2^2 + 3^2 + \ldots + (n-1)^2 + n^2$ using foldMap.

def sumSquares(n: Int): Int = ???

2.6. Write a function hasDivisor which checks if a range contains a multiple of k. Use foldMap and choose f carefully.

def hasDivisor(k: Int, start: Int, stop: Int): Boolean = ???

2.7. We can compute the sum of an area defined by a function within a range a,b (the integral of that function given the range), using the following recursive scheme:

  • if the range is small enough, we treat f as a line (and the area as a trapeze). It's area is $ (f(a) + f(b))(b-a)/2$ .
  • otherwise, we compute the mid of the range, we recursively compute the integral from a to mid and from mid to b, and add-up the result.

Implement the function integrate which computes the integral of a function f given a range:

def integrate(f: Double => Double)(start: Double, stop: Double): Double = ???

Objectives:

  • See some very simple examples of real world Scala

3.1 Interacting with the filesystem. Scala uses Java implementations for using most of the operating system's functionalities. In the snippet below, we see the usage of the Java File class.

import java.io.File
val someFile = new File("somefile.txt")
val fileSize = someFile.length

For this exercise we will want to implement a function that receives two files and returns the file with the maximum size.

3.2 Starting from the snippet below which defines a main function (the entry point of a program) in Scala. we want to make a standalone program that prints the size of a file given as an argument.

object Main {
  def main(args: Array[String]): Unit = {
    println("Hello, Scala developer!")
    if (args.length == 0) {
        println("No parameter :(")
    } else {
        val filename = args(0)
    }
 }
}