Scala尾部递归与命令之间的行为不一致

Scala尾部递归与命令之间的行为不一致,scala,tail-recursion,Scala,Tail Recursion,我目前正在尝试一起学习函数式编程和Scala。我正在移植FORTRAN例程中的一些代码,用于计算相关勒让德多项式的特定修改归一化。我对原始代码的直接命令式翻译是modpLgndr1,我已经对照原始算法进行了检查。我最初尝试以函数形式编写代码是在modpLgdnr2中 import math.pow, abs, sqrt, Pi def xfact(m: Int): Double = { if (m <= 1) 1.0 else { if (m % 2 == 1) m.to

我目前正在尝试一起学习函数式编程和Scala。我正在移植FORTRAN例程中的一些代码,用于计算相关勒让德多项式的特定修改归一化。我对原始代码的直接命令式翻译是modpLgndr1,我已经对照原始算法进行了检查。我最初尝试以函数形式编写代码是在modpLgdnr2中

import math.pow, abs, sqrt, Pi

def xfact(m: Int): Double = {
  if (m <= 1) 1.0
  else {
    if (m % 2 == 1) m.toDouble / sqrt(m.toDouble) * xfact(m - 1)
    else 1.0 / sqrt(m.toDouble) * xfact(m - 1)
  }
}

//this is a very un-scala function....
def modpLgndr1(l: Int, m: Int, x: Double): Double = {
  assert(0 <= m && m <= l && abs(x) <= 1.0)
  val dl = l.toDouble
  val dm = m.toDouble
  val norm = sqrt(2.0 * dl + 1.0) / sqrt(4.0 * Pi)
  var pmm = norm
  if (m != 0) pmm = (pow(-1, m)).toDouble * pmm * xfact(2 * m) * pow((1.0-x * x), (dm / 2.0))
  if (l == m) pmm
  else {
    var pmmp1 = x * pmm * sqrt(2.0 * m + 1.0)
    if (l == m + 1) pmmp1
    else {
      var pll = 0.0
      var dll = 0.0
      for (ll <- m + 2 to l) {
        dll = ll.toDouble
        pll = (x * (2.0 * dll - 1.0) * pmmp1 - sqrt(pow((dll - 1.0), 2.0) - dm * dm) * pmm) / sqrt(pow(dll, 2.0) - pow(dm, 2.0))
        pmm = pmmp1
        pmmp1 = pll
      }
      pll
    }
  }
}

def modpLgndr2(l: Int, m: Int, x: Double): Double = {
  assert(0 <= m && m <= l && abs(x) <= 1.0)
  val dl = l.toDouble
  val dm = m.toDouble
  val norm = sqrt(2.0 * dl + 1.0) / sqrt(4.0 * Pi)
  val pmm = if (m == 0) norm else (pow(-1, m)).toDouble * norm * xfact(2 * m) * pow((1.0-x * x), (dm / 2.0))
  if (l == m) pmm
  else {
    val pmmp1 = x * pmm * sqrt(2.0 * m + 1.0)
    if (l == m + 1) pmmp1
    else {
      def mplacc(ll: Int, acc1: Double, acc2: Double): Double = {
        val dll = ll.toDouble
        val pll = (x * (2.0 * dll - 1.0) * acc2 - sqrt(pow((dll - 1.0), 2.0) - dm * dm) * acc1) / sqrt(pow(dll, 2.0) - pow(dm, 2.0))
        if (ll == m + 2) pll
        else mplacc(ll - 1, acc2, pll)
      }
      mplacc(l, pmm, pmmp1)
    }
  }
}
如果我调用这两个函数,我会得到如下输出:

scala> for (i <- 0 to 10) println(modpLgndr1(10,i,0.2))
0.16685408398957746 -0.2769345073769805 -0.1575129272628402 0.2948210515201088 0.12578847877176355 -0.3292975894931367 -0.058267280378036426 0.37448134558730417 -0.08024600262585084 -0.40389602261165075 0.4424459249420354

scala> for (i <- 0 to 10) println(modpLgndr2(10,i,0.2))
0.16685408398957752 -0.2772969351441124 -0.1578618478786792 0.29654926805696474 0.1349402872678466 -0.33707342609134694 -0.06901634276825179 0.38912154672892657 -0.08024600262585084 -0.40389602261165075 0.4424459249420354

scala> for (i <- 0 to 10) println(modpLgndr2(10,i,0.2))

本质上,对于m=0,l-2,l-1,l,代码是一致的;否则就存在显著差异。这似乎告诉我问题在于调用mplacc函数。在我看来,mplacc就像modpLgndr1中for循环的递归形式。为什么我错了

我想知道你是否在用@Daenyth累积不同的舍入误差。看起来就像你的第一个代码,你从m+2到l累加,在第二个代码中,从l到m-2。浮点加法绝对不是关联的。我希望如果你改变第一个循环,让它从l到m+2乘以-1,两边的结果都是一样的。无论如何,你的计算似乎对舍入误差非常敏感。是的,我读过一篇关于浮点舍入误差影响另一位scala新手的类似文章,但他们的计算结果在小数点后15位或类似的位置!我去看看。