Reputation: 21
I am very new to Scala and I wrote some code using var
. I heard using var
is not recommended in Scala so I want to try using val
only. I researched and tried different ways but could not produce the same result as I used var
.
def yearly_yield(data: List[List[Option[Double]]], balance: Long, index: Int): Long = {
val year = data(index)
var size = 0
var invYield = 0.0
for( j <- year)
if(j.isDefined)
size = size + 1
val amount = balance/size
for( i <- year.indices)
if (year(i).isDefined)
invYield += amount * year(i).get
balance + invYield.toLong
}
and the other one is
def compound_yield(data: List[List[Option[Double]]], balance: Long, index: Int): Long = {
var newBalance = balance
for( i <- 0 until index)
newBalance = yearly_yield(data, newBalance, i)
newBalance
}
I think I can try using recursion for compound_yield
but I am consistently getting errors..
Upvotes: 1
Views: 281
Reputation: 40500
So, yearly is simple:
def yearly(data: List[Option[Double]], balance: Long): Double = {
val interest = year.flatten
interest.map(_ * balance).sum / interest.size
}
You don't need to send all the years, and the index in just to look at one (and random access for List
is not something you should do either way), so I adjusted the parameters accordingly. It should also be returning Double
rather than Long
.
.flatten
here turns List[Option[Double]]
into List[Double]
by throwing out those options that are None
, and unwrapping the others into Double
.
.map
traverses the list, and calls the function for each element (_
is shorthand for the argument, which is the current list element).
Then we add everything together .sum
, and divide by the size (this last part seems wrong, I don't know why you want to do that BTW).
Note, that .flatten
, .map
, .sum
and .size
each traverses the same list, which may be considered wasteful. If year
is a list of months, and there are like 12 of them, it doesn't really matter, and I would urge you to do it this way for the sake of expressiveness. But if we are talking about billions of elements, possibly fetched from remote storage, it may become prohibitive. For that case, there is always .foldLeft
:
val (total, size) = data.foldLeft((0, 0)) {
case ((total, size), Some(elem)) => (total + elem*balance, size + 1)
case (passThrough, _) => passThrough
}
total/size
This does the same as the first version, but with just a single pass through the list.
.foldLeft
traverses the list like .map
does, but instead of just passing the current list element to your function, it passes what it returned the last time (or the initial value, you give it as a parameter for the first call), and the current element, and in the end what you get back is the result of the last function execution, not the entire list.
Now, to compounding. You want to start with a balance, and go through each year, applying the transformation. .foldLeft
does just that:
data.foldLeft(balance) { case (balance, year) => balance + yearly(year, balance) }
Upvotes: 0
Reputation: 51271
Something like this?
def yearly_yield(data :List[List[Option[Double]]]
,balance :Long, index: Int) :Long = {
val year :List[Double] = data(index).flatten
val amount :Long = balance/year.length
year.foldLeft(0.0){case (acc, d) => acc + amount * d}.toLong
}
def compound_yield(data :List[List[Option[Double]]]
,balance :Long, index: Int) :Long =
(0 until index).foldLeft(balance){ case (nb, idx) =>
yearly_yield(data, nb, idx)
}
Upvotes: 1
Reputation: 841
There are a couple things you can improve on here. First of all, your function should just return the yield for one year. If you have a list of years, you can iterate over that and call it on each on. Second, you will improve your Scala code substantially just by familiarizing yourself with the various operations you can perform on collections. In this case, what you're really doing is just a fold operaiton. In general, those are much preferable to loops in Scala. In fact, once you get used to them loops start looking much uglier in comparison! Additionally, because of associativity, you can just multiply by balance/size at the end instead of every time.
def yearly_yield(data: List[Option[Double]], balance: Long): Long =
{
val year = data.flatten //Remove all the None's
(year.fold(0.0)(_+_)*balance/year.size).toLong
}
Your compound yield operation is then just folding the annual yield from left to right.
def compound_yield(data: List[List[Option[Double]]], balance): Long =
data.foldLeft(0l)((balance, year) => balance * yearly_yield(year))
Upvotes: 2