【问题标题】:Stack overflow. Function fails to stop at base case堆栈溢出。功能无法在基本情况下停止
【发布时间】:2014-12-25 18:17:38
【问题描述】:

我有一个名为 Rational 的类:

class Rational(x:Int,y:Int){

def numer=x
def denom=y

def add(r: Rational) =
new Rational(numer* r.denom+ r.numer* denom,
denom* r.denom)

override def toString= numer+ "/"+ denom
def neg = new Rational(-numer, denom)
def -(that: Rational) = add(that.neg)
def < (that:Rational)=numer * that.denom < that.numer * denom
def max(that:Rational)=
if (this < (that)) that else this

def / (r: Rational) = new Rational(numer * r.denom, denom * r.numer)
def numerMIn = new Rational(numer -1, denom)
def denomMIn = new Rational(numer, denom-1)

}

我已经编写了这个函数,它是一个更大函数的子函数。我只是在调试它。 假设 b 是 Rational 3/4,我们将 Rational 2/4 传递给 iter。

def iter(c:Rational): Rational={

        if (c.denom==0) new Rational(0,0) else if(!(c < b)) denomDec(c) else c add iter(denomDec(c))

    }

这里是denomDec:

def denomDec(r: Rational) = new Rational(r.numer, r.denom-1)

问题是iter函数没有停止并运行到Stack Overflow。

这是在 iter() 内部发生的事情。它得到 2/4 作为参数。然后itake 2/4 + 2/3 + 当它达到2/2时,它调用denomDec在2/2之后得到2/1,因为后者不能小于3/4。然后 denomDec 达到 2/0,在这里它应该返回 new Rational(0,0) 但它没有这样做,这反过来又导致 Stack Overflow。

我的问题是,为什么函数在遇到if denomDec(r: Rational) = new Rational(r.numer, r.denom-1) 的基本情况时重复出现时不会停止

为了澄清:示例 iter(2/4) 应该这样做:2/4 + 2/3 + 0/0。 2/2 和 2/1 被跳过,因为它们都大于 3/4。

【问题讨论】:

  • 试过iter(2/0)?如果那个停止,那么你的问题是你的递归没有达到那个点。
  • 它确实做到了这一点。我已经测试过了。它的作用是不断调用 denomDec 所以你得到 2/0, 2/-1, 2/-2....
  • 这段代码有很多问题,它完全可以工作真是一个奇迹。 Rational(0, 0) 不应该被允许存在,&lt; 不能正常工作,这几乎破坏了所有其他方法。考虑:val a = new Rational(1,2) , val b = new Rational(3,-4), a
  • 忘了告诉你你对否定的 Rationals 不感兴趣,所以否定的部分是无关紧要的。当 Rational 的分母为 0 时,您必须返回 0/0,这对于这个特定问题是可以的。我只是不明白为什么这个递归不会停止。
  • 它似乎对我有用:scala&gt; iter(new Rational(2,4)) res0: Rational = 38/12 'b' 在迭代过程中会改变吗?

标签: scala recursion stack-overflow


【解决方案1】:

Rational(0,0) 不为零。当您对其进行数学运算时,您会得到不好的结果。考虑 Rational(0,0)

在 iter() 中,我们的第二个 if 语句在计算 Rational(0,0) 并且 Rational(0,0) 被传递给 denonDec 并且其分母变为负数时不正确。一旦发生这种情况,你就会沉没。

我会在 Rational 构造函数中设置一个守卫,以防止 denom 小于 1。我会将 Rational(0,1) 用作零。从基本算术中记住分数 0/0 是未定义的。

【讨论】:

    【解决方案2】:

    也许您的问题超出了提出的范围; 我已经测试了代码,但没有收到错误:

     iter(new Rational(2,4))                         
      //> denomDec | old / new denom 4 / 3
      //| denomDec | old / new denom 3 / 2
      //| iter | c<b
      //| denomDec | old / new denom 2 / 1
      //| add | adding two rationals: 2/3, 2/1
      //| add | it gives: 8/3
      //| add | adding two rationals: 2/4, 8/3
      //| add | it gives: 38/12
      //| res0: test.Rational = 38/12   
    
     iter(new Rational(2,0))
        //> iter | denom=0
      //| res0: test.Rational = 0/0      
    
     iter(new Rational(0,4))
     //> denomDec | old / new denom 4 / 3
      //| denomDec | old / new denom 3 / 2
      //| denomDec | old / new denom 2 / 1
      //| denomDec | old / new denom 1 / 0
      //| iter | denom=0
      //| add | adding two rationals: 0/1, 0/0
      //| add | it gives: 0/0
      //| add | adding two rationals: 0/2, 0/0
      //| add | it gives: 0/0
      //| add | adding two rationals: 0/3, 0/0
      //| add | it gives: 0/0
      //| add | adding two rationals: 0/4, 0/0
      //| add | it gives: 0/0
      //| res0: test.Rational = 0/0
    

    【讨论】:

    • 问题是它没有做它应该做的事情。它必须这样做:2/4 + 2/3 +0/0 跳过 2/2 和 2/1,因为这两个大于 3/4。在@guzial 击中 2/0 后,代码不应该继续运行
    【解决方案3】:

    我发现上面的代码有什么问题。 而不是else if(!(c &lt; h)) denomDec(c),我应该做else if(!(c &lt; h)) iter(denomDec(c)),这样我实际上用更小的参数再次调用iter()函数。 我的另一个错误是试图将 0/0 添加到另一个 Rational。问题是,如果您查看我添加def add(r: Rational) = new Rational(numer* r.denom+ r.numer* denom,denom* r.denom) 的代码,您会发现将0/0 添加到任何Rational 将始终返回0/0。因此,当达到基本情况时,我需要返回 new Rational(0,0) 以外的其他内容。

    【讨论】:

      猜你喜欢
      • 2012-04-23
      • 1970-01-01
      • 2012-05-01
      • 2014-07-29
      • 1970-01-01
      • 2011-07-17
      • 2019-05-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多