摆脱Scala中的for循环

时间:2018-10-11 11:42:12

标签: scala functional-programming

这是一个涉及n的问题。对于给定的数字(1 / n!) * (1! + 2! + 3! + ... + n!) ,找到以下答案:

object MyClass {

def fsolve(n: Int): Double = {
    var a: Double = 1
    var cum: Double = 1
    for (i <- n to 2 by -1) { 
        a = a * (1.0/i.toDouble)
        cum += a
    }
    scala.math.floor(cum*1000000) / 1000000
}

def main(args: Array[String]) {
    println(fsolve(7))     // answer 1.173214
}

Scala中的迭代解决方案非常简单–一个简单的for循环就足够了。

foldLeft

}

我想摆脱for循环并使用foldLeft操作。由于此想法是将数字列表简化为单个结果,因此应该使用object MyClass { def fsolve(n: Int) = { (n to 2 by -1).foldLeft(1.toDouble) (_*_) // what comes next???? } def main(args: Array[String]) { println(fsolve(7)) } } 或类似的指令。怎么样?我正在努力寻找一个值得遵循的出色Scala示例。下面的代码说明了我在努力转向更惯用的Scala的过程。

{{1}}

有任何建议或解决方案的指针吗?

3 个答案:

答案 0 :(得分:2)

结果是从foldLeft返回的,就像这样:

val cum = (n to 2 by -1).foldLeft(1.toDouble) (_*_)

仅在您需要的情况下,该函数需要有所不同,因为上述折叠将所有i值相乘。您将同时传递cuma的折叠值:

def fsolve(n: Int): Double = {
  val (cum, _) = (n to 2 by -1).foldLeft(1.0, 1.0) { case ((sum, a),i) =>
    val newA = a * (1.0/i.toDouble)
    (sum + newA, newA)
  }
  scala.math.floor(cum*1000000) / 1000000
}

答案 1 :(得分:1)

您提供的公式可以很好地映射到scanLeft函数。它的工作方式类似于foldLeftmap的组合,运行折叠操作,但将每个生成的值存储在输出列表中。以下代码生成从1n的所有阶乘,并将它们相加,然后除以n!。请注意,通过在最后而不是在每个中间步骤执行一次浮点除法,可以减少浮点错误的几率。

def fsolve(n: Int): Double =
{
  val factorials = (2 to n).scanLeft(1)((cum: Int, value: Int) => value*cum)
  scala.math.floor(factorials.reduce(_+_)/factorials.last.toDouble*1000000)/1000000
}

答案 2 :(得分:0)

我将尝试通过提出不同的方法来实现解决方案,而不是填补空白。

def fsolve(n: Int): Double = {
  require(n > 0, "n must be positive")
  def f(n: Int): Long = (1 to n).fold(1)(_ * _)
  (1.0 / f(n)) * ((1 to n).map(f).sum)
}

在函数中,确保使用require输入无效,我定义了阶乘(如f),然后通过以最接近原始函数的方式简单地写下函数来使用它我们要实现的表达式:

(1.0 / f(n)) * ((1 to n).map(f).sum)

如果您确实想显式折叠,则可以按以下方式重写此表达式:

(1.0 / f(n)) * ((1 to n).map(f).fold(0L)(_ + _))

此外,请注意,由于您正在执行的所有操作(加法和乘法)都是可交换的,因此可以使用fold代替foldLeft:使用前者并没有规定操作的顺序应该运行,从而允许集合的特定实现并行运行计算。

您可以使用此代码here on Scastie