Scala如何使用val而不是var

时间:2018-12-14 22:00:50

标签: scala increment var

我对Scala并不陌生,我使用var编写了一些代码。我听说在Scala中不建议使用var,所以我只想尝试使用val。我研究并尝试了不同的方法,但无法获得与使用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
}

另一个是

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
}

我认为我可以尝试对compound_yield使用递归,但是我总是遇到错误。

3 个答案:

答案 0 :(得分:2)

您可以在此处进行一些改进。首先,您的函数应该只返回一年的收益。如果您有年份列表,则可以对其进行迭代并逐年调用。其次,仅通过熟悉可以对集合执行的各种操作,就可以大大改善Scala代码。在这种情况下,您真正​​要做的只是折叠操作。通常,那些比Scala中的循环更可取。实际上,一旦您习惯了它们,相比之下,循环看起来就难看得多!此外,由于具有关联性,您可以在末尾乘以余额/大小而不是每次乘以。

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
}

您的复合收益率操作只是将年收益率从左向右折叠。

def compound_yield(data: List[List[Option[Double]]], balance): Long = 
  data.foldLeft(0l)((balance, year) => balance * yearly_yield(year))

答案 1 :(得分:1)

像这样吗?

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)
  }

答案 2 :(得分:0)

因此,每年都很简单:

def yearly(data: List[Option[Double]], balance: Long): Double = {
  val interest = year.flatten
  interest.map(_ * balance).sum / interest.size
}

您不需要发送所有的年份,而只是查看索引就可以索引(并且List的随机访问不是您应该采用的任何一种方式),因此我相应地调整了参数。它也应该返回Double而不是Long

.flatten通过抛弃List[Option[Double]]的那些选项,并将其他选项展开为List[Double],将None变成Double.map遍历列表,并为每个元素调用函数(_是参数的简写形式,即当前列表元素)。 然后我们将所有内容加在一起.sum,然后除以大小(最后一部分似乎是错误的,我不知道您为什么要这样做BTW)。

请注意,.flatten.map.sum.size分别遍历相同的列表,这可能被认为是浪费的。如果year是几个月的清单,并且大约有12个月,那没什么关系,为了表达力,我敦促您以这种方式这样做。但是,如果我们谈论的是可能从远程存储中获取的数十亿个元素,则它可能变得令人望而却步。在这种情况下,总是有.foldLeft

 val (total, size) = data.foldLeft((0, 0)) { 
   case ((total, size), Some(elem)) => (total + elem*balance, size + 1)
   case (passThrough, _) => passThrough
 }
 total/size

此功能与第一个版本相同,但仅需通过列表一次即可。 .foldLeft.map一样遍历列表,但是它不仅将当前列表元素传递给函数,还传递了上次返回的内容(或初始值,您将其作为参数提供给第一次调用),当前元素,最后返回的是最后一个函数执行的结果,而不是整个列表。

现在,要复利。您想从平衡开始,然后每年进行一次转换。 .foldLeft就是这样做的:

 data.foldLeft(balance) { case (balance, year) => balance + yearly(year, balance) }