Noooooobie
Noooooobie

Reputation: 21

scala how to use val instead of var

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

Answers (3)

Dima
Dima

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

jwvh
jwvh

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

Ethan
Ethan

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

Related Questions