Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/475.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 创建递归函数上限的好方法和好语言是什么?_Javascript_Recursion - Fatal编程技术网

Javascript 创建递归函数上限的好方法和好语言是什么?

Javascript 创建递归函数上限的好方法和好语言是什么?,javascript,recursion,Javascript,Recursion,我听说递归有可能溢出或内存泄漏,因为每个函数调用都需要一些内存使用。我想,即使我不会使内存溢出,我可能仍然希望限制函数调用的数量。因此,我使用Javascript和throw创建了这个阶乘函数,但我想知道是否有一种更自然的方法来实现它,或者是否有更适合这种工作的编程语言?。是否有一些解决方案可以改进此类算法的线性内存使用 const CAPSIZE = 5 function factorial_req(a) { if( a == 1 ){ factorial_req.stack_sl

我听说递归有可能溢出或内存泄漏,因为每个函数调用都需要一些内存使用。我想,即使我不会使内存溢出,我可能仍然希望限制函数调用的数量。因此,我使用Javascript和throw创建了这个阶乘函数,但我想知道是否有一种更自然的方法来实现它,或者是否有更适合这种工作的编程语言?。是否有一些解决方案可以改进此类算法的线性内存使用

const CAPSIZE = 5
function factorial_req(a) {
  if( a == 1 ){
    factorial_req.stack_slack = CAPSIZE
    return 1
  }
  else if( factorial_req.stack_slack == 0 ){
    throw 'stack cap'
  }
  else {
    factorial_req.stack_slack--
    return a * factorial_req(a-1)
  }
}
factorial.stack_slack = CAPSIZE
function factorial(a) {
  try {
    return factorial_req(a)
  }
  catch(err) {
    if( err == 'stack cap' )
      return 0 // 0 == flase
    else
      throw err
  }
}
// test
console.log( `5! = ${factorial(5)} , !3 = ${factorial(3)} and !9 == 0 is ${factorial(9) == 0} and !9 == false is ${factorial(9) == false} while !99999999 == true is ${factorial(99999999) == true}`)

您编写的算法使用线性空间。你不能在线性空间内实现你的算法。关键是使用尾部递归算法。尾部递归的正确实现不会导致线性空间的使用

考虑用伪代码编写的阶乘函数的基本实现

factorial(n) = 1 if n == 0
               n * factorial(n - 1) otherwise
这基本上就是阶乘函数的定义。然后,
factorial(3)
的计算如下:

factorial(3)

3*阶乘(2)

3*(2*阶乘(1))

3*(2*(1*(阶乘(0))

3*(2*(1*1))

3*(2*1)

3*2

6

请注意,算法的本质就是需要线性空间

现在考虑一个不同的阶乘实现。我们要编写一个帮助函数,名为<代码> FracealAlgHelpB/<代码>,它满足属性<代码>阶乘辅助器(ACC,N)=ACC*N!< /代码>。这给了我们下面的算法:

factorial(n) = factorial_helper(1, n)

factorial_helper(acc, n) = acc if n == 0
                           factorial_helper(acc * n, n - 1) otherwise
<>现在考虑<代码>因子(3)< /代码>

的评价。
factorial(3)

factorial\u helper(1,3)

factorial\u helper(3,2)

factorial\u helper(6,1)

factorial\u helper(6,0)

6

请注意,
factorial(n)
的计算在第二个实现中使用常量空间(假设整数占用常量空间)。这是因为
factorial\u helper
是尾部递归的。这意味着在计算
factorial\u helper(a,b)时
,我们进行递归调用,递归调用的结果正好是
factorial\u helper(a,b)
(没有额外的处理)。换句话说,
factorial\u helper
中的递归调用,如果发生,就是“factorial\u helper所做的最后一件事”

不幸的是,JavaScript目前不能保证正确实现尾部递归,这使得它通常不适合用于递归

通过切换到连续传递样式,始终可以迭代地实现递归函数。类似Haskell的语言使这非常容易。例如,在Haskell中实现阶乘如下所示:

factorial n=如果n==0
那么1
else n*阶乘(n-1)
在连续传球方式中,这看起来像

factorial n=如果n==0
然后返回1
else(n*)阶乘(n-1)

这是一个非常小的语法更改,但是factorial现在使用continuations实现,因此不会出现堆栈溢出(尽管它仍然在
n
中使用线性内存).

递归函数的要点是一直运行,直到满足某个条件。您还需要在其内部调用第二个函数不能满足要求的相同函数。通常情况下,尾部调用优化(TCO)有助于递归算法不溢出。在不支持TCO的语言/环境中,可以使用手动实现。简而言之,可以将递归函数转换为返回a,并将其称为
trampoline(recursiveFn())
然后蹦床处理堆栈调用。
throw
绝对不是处理递归的最佳方式。“有可能溢出或内存泄漏”-简单循环也是如此。顺便说一句,在特定深度引发异常与运行时引发堆栈溢出异常没有什么不同。如果您关心内存或性能,或者任何其他指标,只需将其作为循环写入即可。我不确定向递归添加上限是否有任何意义。运行时在你的递归太深了;为什么不使用它呢?内存泄漏和内存不足是两码事。只有当你丢失了对资源的所有引用时,才会发生内存泄漏,这在这里是不会发生的。