【问题标题】:What is the Definition of Recursion递归的定义是什么
【发布时间】:2013-07-15 17:46:37
【问题描述】:

所以,我们都知道递归函数,对吧?但究竟是什么使函数递归?我在另一个问题 (Lightening effect with javascript) 的评论部分进行了一次小型讨论,这在某种程度上挑战了我对递归函数的看法,但它也让我对缺乏正确的定义感到非常不满。

到目前为止,我个人对递归函数的定义如下:

如果函数直接或间接调用自身,则它是递归的

注意:我将为以下示例提供 JavaScript 代码,但我确信它们非常通用。

这种递归函数的一个简单示例如下:

function a() {
    a();
}

还有这个:

function a() {
    b();
}

function b() {
    a();
}

甚至这个:

function a() {
    setTimeout(a, 1000);
}

这些函数都不计算任何东西,但我仍然认为它们是递归的,因为它们调用了自己。

出现的一件事是,第三个示例不是递归的,因为它使用setTimeout,因此堆栈被展开。它也不是递归的,因为在从 nth 调用返回后,它不会将控制权交还给 n-1th 调用。

提出的另一点是,这些函数都不是递归的,因为它们都不以递归方式计算实际问题。这意味着必须通过将问题划分为越来越小的实例来解决问题。这里引用维基百科的文章:

计算机编程中的递归以函数为例子 以更简单、通常更小的自身版本来定义。这 然后通过组合解决方案来设计问题的解决方案 从问题的简单版本中获得。

所以这是递归的:

function fac(n) {
    if (n <= 0) {
        return 1;
    }
    return n * fac(n - 1);
}

但这不会是:

function fac(n) {
    if (n <= 0) {
        return 1;
    }
    console.log(n);
    fac(n - 1);
}

那么递归函数的正确定义是什么?有没有,还是真的只是一个哲学问题?函数必须具备哪些特征才能被归类为递归?

【问题讨论】:

    标签: javascript recursion definition


    【解决方案1】:

    递归的定义?再读一遍这一行,直到你明白为止。

    (具有中止标准以防止无限循环的自调用函数)。

    【讨论】:

    • 所以如果它没有中止标准,它就不再是递归的了吗?
    • “递归的定义?再读一遍这行,直到你明白为止。” - 对此进行投票,直到我的鼠标按钮中断! :D
    • @basilikum 是,但没用,因为它永远不会终止。
    • 我实际上只是意识到答案中的递归;)是的,有道理。那么自称真的是唯一的标准吗?那么setTimeout 的例子呢?
    【解决方案2】:

    递归只是根据该问题的更简单情况(更简单的意思是“更接近”终止条件,不一定实际上更简单)来定义问题,直到最简单的情况是一个已知的(前面提到的终止条件)。因此,例如,常年阶乘函数有一个终止条件:

    f(1) = 1
    

    以及用更简单的方式定义问题:

    f(n) = n * f(n - 1), for n > 1
    

    我听过的最好的解释是这样的:

    1. 如果您是 Donald Knuth,您就会明白它是什么。
    2. 否则,找一个离唐纳德更近的人问问他们。

    我不会调用setTimeout 一次递归,因为a 不是实际上调用了它自己。相反,它要求“系统”在以后调用它。

    在某处有终止条件也很重要。没有它,它仍然是递归,但它是 infinite 递归,与无限循环没有什么不同,例如:

    for (i = 0; i < 10; j++) {}
    

    因此除了测试堆栈溢出时会发生什么之外,其他任何事情都不太可能有任何好处:-)

    【讨论】:

    • 好老克努斯。我还没听过这个。
    • 那么一个调用自己的函数,只是为了增加一个计数器来遍历一个平面数组,不会是递归的吧?
    • @basilikum,是的,它会是 - 它在数组的末尾具有终止点,并且它具有根据“更简单”函数定义的函数。这是一种低效的方法,因为堆栈是一种相当有限的资源,但它仍然是递归的,就像def sum (a,b) = sum (a+1,b-1) if b &gt; 0, else a(当然对于无符号数)一样。除非您可以加或减 1,否则添加两个数字不是一个聪明的方法,但它仍然是递归。
    • 好的,那么递归确实是我的问题解决策略的一个特征,而不是我在编程语言中构建的一个特征......如果这有意义吗?
    • @basilikum,它既是一种思维方式(纯数学也有这个概念,即使在没有计算机的情况下),也是一种在算法和语言中实现这种思维的方式。
    【解决方案3】:

    递归是赋予调用自身的函数的名称。现在无论函数是否无限调用自身......它仍然是一个递归。

    不一定要将问题划分为子问题。但是,在计算机科学中;递归一词是指一种用于解决问题的技术,通过将问题分解为子问题,通常问题是有限的。

    还有一点,递归是使用堆栈实现的。每个函数调用在堆栈中堆叠在另一个顶部,直到最后一个调用满足基本条件,然后堆栈中的函数从上到下执行。

    但是,如果没有基本条件或永远不会满足基本条件。然后对该函数的无限调用将被推送到堆栈,导致内存被填满,并且将引发 stackOverFlow 异常,操作系统通过终止程序来处理它。

    对于setTimeout()是异步调用,与递归无关,是独立调用,因为调用者函数不依赖于被调用者是否相同函数被调用或其他。

    【讨论】:

      【解决方案4】:

      来自您发布的维基百科:

      当一个函数被定义为更简单、通常更小的版本时,计算机编程中的递归就是一个例子。然后通过结合从问题的简单版本中获得的解决方案来设计问题的解决方案。

      所以。有问题,就有解决办法。有一个自称为最小化主要问题的函数。这个函数是递归的。

      案例:

      function a() {
          a();
      }
      

      没有问题,没有什么可以最小化,没有解决方案。这对我来说不是递归的。这只是一个无限循环。

      再举一个例子:

      function a(n) {
          if(n<.5) {
              return n+a(Math.random());
          }else {
              return n;
          }
      }
      console.log(a(.3));
      

      这是递归的吗? 不。 可能存在问题,但找不到解决方案以最小化主要问题。很简单,它会随机调用自己,直到某个标志为真。同样,这是一个循环。 setTimeout 或 setInterval 也是如此(问题的解决取决于系统调用)。

      【讨论】:

        猜你喜欢
        • 2015-11-22
        • 2023-01-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-02
        • 2017-09-22
        • 1970-01-01
        • 2011-04-10
        相关资源
        最近更新 更多