Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/380.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_Functional Programming - Fatal编程技术网

Javascript 如何重构二进制递归以使其与蹦床函数兼容?

Javascript 如何重构二进制递归以使其与蹦床函数兼容?,javascript,recursion,functional-programming,Javascript,Recursion,Functional Programming,我编写了如下快速排序函数: const quickSort = list => { if (list.length === 0) return list const [pivot, ...rest] = list const smaller = [] const bigger = [] for (x of rest) { x < pivot ? smaller.push(x) : bigger.push(x) } return [...quick

我编写了如下快速排序函数:

const quickSort = list => {
  if (list.length === 0) return list
  const [pivot, ...rest] = list
  const smaller = []
  const bigger = []
  for (x of rest) {
    x < pivot ? smaller.push(x) : bigger.push(x)
  }

  return [...quickSort(smaller), pivot, ...quickSort(bigger)]
}

如何转换我的
快速排序
功能,使其利用
蹦床
功能?

我看不出蹦床如何使您的
快速排序
更有效。如果您处于返回状态、具有值或处于进一步分割结果状态、具有函数,那么您将添加的最少内容是基于硬字符串的检查。这将大大增加计算时间

你能做的就是让它更通用。一般来说,我会说蹦床是一个很好的解释递归的方法,但与直接函数调用相比,在效率方面从来都不好

此外,要利用蹦床,必须创建一个返回函数或值的函数。但你需要s.th。可以返回除法(在
快速排序中有两个递归子调用)。这就是你们需要重用蹦床的方式(你们的递归中的一种递归,你们可以称之为二级递归)

constqs=(列表)=>{
如果(list.length==0)
退货清单;
常量[pivot,…rest]=列表;
常数较小=[];
常量更大=[];
对于(让x表示休息){
x(…args)=>{
让结果=fn(…args);
while(结果类型==“函数”){
结果=结果();
}
返回结果;
};
控制台日志(蹦床(qs)([1,6,2,4]);

原木(蹦床(qs)([4,5,6,1,3,2])要使用蹦床,递归函数必须是。您的
快速排序
函数是尾部递归函数,因为对
快速排序
的递归调用不会出现在尾部位置,即

return [...quickSort(smaller), pivot, ...quickSort(bigger)]
也许在程序中很难看到,但是程序中的尾部调用是一个数组concat操作。如果您不使用ES6语法编写它,我们可以更容易地看到这一点

const a = quickSort(smaller)
const b = quickSort(bigger)
const res1 = a.concat(pivot)
const res2 = res1.concat(b) // <-- last operation is a concat
return res2
现在我们可以看到,
quickSort
总是出现在尾部位置。但是,如果我们使用大量输入调用函数,直接递归将导致许多调用帧累积,并最终溢出堆栈。为了防止这种情况发生,我们在蹦床上弹跳每个尾巴

const quickSort = (list, cont) => {
  if (list.length === 0)
    return bounce (cont, list);

  const [pivot, ...rest] = list
  const smaller = []
  const bigger = []
  for (const x of rest) {
    x < pivot ? smaller.push(x) : bigger.push(x)
  }

  return bounce (quickSort, smaller, a =>
           bounce (quickSort, larger, b =>
             bounce (cont, [...a, pivot, ...b])))
}
果然管用

console.log (trampoline (quickSort ([ 6, 3, 4, 8, 1, 6, 2, 9, 5, 0, 7 ])))
// [ 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 ]
我们验证了它是否适用于大数据。一百万个介于零和一百万之间的数字

const rand = () =>
  Math.random () * 1e6 >> 0

const big = 
  Array.from (Array (1e6), rand)

console.time ('1 million numbers')
console.log (trampoline (quickSort (big)))
console.timeEnd ('1 million numbers')
// [ 1, 1, 2, 4, 5, 5, 6, 6, 6, 7, ... 999990 more items ]
// 1 million numbers: 2213 ms

在另一个问题中,我展示了将另外两个常用函数转换为连续传递样式的过程


堆栈安全递归(Stack safe recursion,Stack safe recursion,堆栈安全递归)是我广泛讨论过的话题,我喜欢这个q,因为它是一个非常好的教学示例,具有漂亮的现代js和函数式编程(upvote)。蹦床不会提高效率——事实上,几乎在所有情况下,它的性能都可能较低。使用蹦床的唯一原因是为了防止递归程序破坏堆栈。谢谢!我检查了蹦床(qs)和原始快速排序的性能,结果发现后者稍微快一点。我还发现快速排序的命令式版本比递归版本性能更好。所以,如果性能是一个问题,我们应该避免使用递归吗?就我所记得的,所谓的“结束递归”总是可以转换成迭代。是的,如果可以的话,更喜欢迭代。迭代和递归在性能上可能存在巨大差异。最著名的例子是fib(x)。进一步检查O形符号!这是不能使用蹦床的。在您的程序中,
trampoline
调用
qs
哪个调用
trampoline
哪个调用
qs
。。。这是错误的,如果输入数据很大,您仍然会遇到堆栈溢出enough@user633183回答已经提到了递归问题和堆栈问题(但感谢精确的英语术语,我的学习时间已经很久了)@user633183在阅读了一些内容后:我明白你指的了。的确,我的答案是纯粹从句法的角度来修正它。它不是将其优化为尾部递归(我记得德语中的术语“Endrekursion”),然后将其转换为迭代,从而消除堆栈问题。
const bounce = (f, ...args) =>
  ({ tag: bounce, f, args })

const trampoline = t =>
{ while (t && t.tag === bounce)
    t = t.f (...t.args)
  return t
}
console.log (trampoline (quickSort ([ 6, 3, 4, 8, 1, 6, 2, 9, 5, 0, 7 ])))
// [ 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9 ]
const rand = () =>
  Math.random () * 1e6 >> 0

const big = 
  Array.from (Array (1e6), rand)

console.time ('1 million numbers')
console.log (trampoline (quickSort (big)))
console.timeEnd ('1 million numbers')
// [ 1, 1, 2, 4, 5, 5, 6, 6, 6, 7, ... 999990 more items ]
// 1 million numbers: 2213 ms