- Scala Programming Projects
- Mikael Valot Nicolas Jorand
- 574字
- 2021-07-23 16:25:19
Implementing futureCapital
We now have a failing test, so it is time to make it pass by writing the production code. If we use initialCapital = 10,000 and monthlySavings = 1,000, the computation we need to perform can be decomposed as follows:
- For month 0, before any savings, we have capital0 = initialCapital = 10,000.
- For month 1, our initial capital generated some interest. We also saved 1,000 more. We therefore have capital1 = capital0 *(1 + monthlyInterestRate) + 1,000
- For month 2, we have capital2 = capital1 *(1 + monthlyInterestRate) + 1,000
There is a mathematical formula to compute capitalN from the parameters, but we will not use it here. This formula works well for fixed interest rates, but we will use variable interest rates later in this chapter.
Here is the body of the function:
def futureCapital(interestRate: Double, nbOfMonths: Int, netIncome: Int, currentExpenses: Int, initialCapital: Double): Double = {
val monthlySavings = netIncome - currentExpenses
def nextCapital(accumulated: Double, month: Int): Double =
accumulated * (1 + interestRate) + monthlySavings
(0 until nbOfMonths).foldLeft(initialCapital)(nextCapital)
}
We first generate a collection of integers using 0 to nbOfMonths, and we then iterate through it using foldLeft. foldLeft is one of the most powerful functions in the Scala collection library. Many other functions in the collections library could be implemented just by using foldLeft, such as reverse, last, contains, sum, and so on.
In the Scala SDK, the signature of foldLeft is as follows:
def foldLeft[B](z: B)(op: (B, A) => B): B
You can see its definition in IntelliJ by pointing at it with the mouse and using cmd + left-click. This introduces some new syntax:
- [B] means that the function has a type parameter named B. When we call the function, the compiler automatically infers what B is, depending on the type of the z: B argument. In our code, the z argument is initialCapital, of type Double. Therefore, our call to foldLeft in futureCapital will behave as if the function was defined with B = Double:
def foldLeft(z: Double)(op: (Double, A) => Double): Double.
- The function has two parameter lists. Scala allows you to have many parameter lists. Each list can have one or many parameters. This does not change the behavior of the function; it is just a way of separating the concerns of each parameter list.
- op: (B, A) => B means that op must be a function that has two parameters of type B and A and returns a value of type B. Since foldLeft is a function that takes another function as an argument, we say that foldLeft is a higher order function.
If we consider a coll collection, foldLeft works as follows:
- It creates a var acc = z accumulator then calls the op function:
acc = op(acc, coll(0))
- It carries on calling op with each element of the collection:
acc = op(acc, coll(i))
- It returns acc once it has iterated through all elements of the collection
In our futureCapital function, we pass op = nextCapital. The foldLeft will iterate through all Int between 1 and nbOfMonths, each time computing the capital using the previous capital. Note that, for now, we do not use the month parameter in nextCapital. We must declare it, though, because the op function in foldLeft must have two parameters.
You can now run the RetCalcSpec unit test again. It should pass.