尾调用优化javascript

时间:2017-06-24 18:39:53

标签: javascript recursion optimization

注意:这只是为了学习和改善自己。我知道数组的可用排序方法。我只想尝试降低TCO的基础知识。

目前正在尝试使用递归处理排序算法。但是当我尝试处理大型数据集(+4000个对象)时,我仍然遇到堆栈溢出错误。我试图实施TCO。我对这个概念比较陌生,但我认为我有它的要点。但是,我仍然收到堆栈溢出错误。

const sort = (arr, counter) => {
  if (!counter) {
    counter = arr.length - 1;
  }
  for (let n = 1; n <= counter; n++) {
    if(arr[n - 1] < arr[n]) {
      let placeHolder = arr[n];
      arr[n] = arr[n - 1];
      arr[n - 1] = placeHolder;
    }
  }
  counter -= 1;
  return counter === 0 ? arr : sort(arr, counter);
};

function sortRecursive(arr) {
  return sort(arr);
}

更新:

我设法让它工作,但我不太明白为什么。我设法处理100,000次递归没有问题。我不得不移动检查计数器是否已定义的布尔值。但是,我不太明白为什么会这样做。

const sort = (arr, counter) => {
  if (!counter) {
    counter = arr.length - 1;
  }
  for (let n = 1; n <= counter; n++) {
    if(arr[n - 1] < arr[n]) {
      let placeHolder = arr[n];
      arr[n] = arr[n - 1];
      arr[n - 1] = placeHolder;
    }
  }
  counter -= 1;
  if (counter === 0) {
    return arr;
  } else {
    return sort(arr, counter);
  }
};

function sortRecursive(arr) {
  return sort(arr, arr.length - 1);
}

输出:

let firstArr = [];
let secondArr = [];

for (let x = 0; x < 100000; x++) {
  firstArr.push(Math.ceil(Math.random() * 100000));
  secondArr.push(Math.ceil(Math.random() * 100000));
}

sortRecursive(firstArr);
//Array[100000]

3 个答案:

答案 0 :(得分:2)

正如您可能知道的,尾调用优化是一种编译器技术,它可以允许程序通过不为每个递归调用分配更多内存来无限递归。

Javascript 目前尾调优化,但语言规范的ES2015标准包括TCO。每次函数在Javascript中调用自身时,都会创建一个新的堆栈帧,分配新的内存,因此最终会耗尽并崩溃。

有一些技术可以避免这种情况,包括trampolines而不使用递归循环。但目前你无法在Javascript中无限递归。

答案 1 :(得分:0)

为什么你需要递归(它永远不会用4000+元素,因为这超过了每个调用堆栈)?你不能这样做:

const sort = (arr) => {
     var counter = arr.length;
     while(counter-->0){
       for (let n = 1; n <= counter; n++) {
           if(arr[n - 1] < arr[n]) {
              let placeHolder = arr[n];
              arr[n] = arr[n - 1];
              arr[n - 1] = placeHolder;
           }
        }
     }
     return arr;
}

如果你想要种类的递归,你可以使用 qeue 来保持堆栈为空(需要传递一个回调):

setTimeout(sort,0,arr,counter);//instead of sort(arr,counter);

顺便说一句,更容易,更快(因为它原生实现):

arr.sort((a,b)=>a-b);

答案 2 :(得分:0)

确定您是否正在进行尾调用优化?

这是使用您更新的代码进行的测试。我改变的唯一事情是:

  1. 添加id以将代码置于严格模式。某些支持TCO的浏览器可能需要严格模式才能使用TCO。
  2. 添加import PIL from PIL import Image, ImageTk from tkinter import * from tkinter import filedialog from tkinter import messagebox root = Tk() class Window: def __init__(self, master=None): tower = PIL.Image.open("Images/island.png") master.update() win_width = int(master.winfo_width()) win_height = int(master.winfo_height()) # Resize the image to the constraints of the root window. tower = tower.resize((win_width, win_height)) tower_tk = ImageTk.PhotoImage(tower) # Create a label to hold the background image. canvas = Canvas(master, width=win_width, height=win_height) canvas.place(x=0, y=0, anchor='nw') canvas.create_image(0, 0, image=tower_tk, anchor='nw') canvas.image = tower_tk frame = Frame(master) frame.place(x=win_width, y=win_height, anchor='se') master.update() w = Label(master, text="Send and receive files easily", anchor='w') w.config(font=('times', 32)) w.place(x=0, y=0, anchor='nw') master.title("Bifrost v1.0") self.img1 = PhotoImage(file="Images/logo.png") self.img2 = PhotoImage(file="Images/magnifier.png") frame.grid_columnconfigure(0, weight=1) sendButton = Button(frame, image=self.img2) sendButton.grid(row=0, column=1) sendButton.image = self.img2 receiveButton = Button(frame, image=self.img1) receiveButton.grid(row=0, column=2) receiveButton.image = self.img1 menu = Menu(master) master.config(menu=menu) file = Menu(menu) file.add_command(label='Exit', command=self.client_exit) menu.add_cascade(label='File', menu=file) edit = Menu(menu) edit.add_command(label='abcd') menu.add_cascade(label='Edit', menu=edit) help = Menu(menu) help.add_command(label='About Us', command=self.about) menu.add_cascade(label='Help', menu=help) def callback(): path = filedialog.askopenfilename() e.delete(0, END) # Remove current text in entry e.insert(0, path) # Insert the 'path' # print path w = Label(root, text="File Path:") e = Entry(root, text="") b = Button(root, text="Browse", fg="#a1dbcd", bg="black", command=callback) w.pack(side=TOP) e.pack(side=TOP) b.pack(side=TOP) def client_exit(self): exit() def about(self): message = "This is a project developed by Aditi,Sagar and" message += "Suyash as the final year project." messagebox.showinfo("Delete Theme", message) root.resizable(0,0) #size of the window root.geometry("700x400") app = Window(root) root.mainloop() 以在每次'use strict';来电时打印调用堆栈。
  3. 由于上述评论中提到的原因,将测试阵列设置更改为使用console.trace()而不是sort()
  4. 将数组长度更改为10。
  5. 在运行代码段之前打开开发人员控制台并观察调用堆栈跟踪。

    我在最新版本的Chrome 59.0.3071.109,Firefox 54.0和Edge 15.15063中对此进行了测试。所有这些堆栈跟踪显示每次调用时调用堆栈都在增长,表明没有尾调用优化。

    只是为了踢,我还在Chrome中使用Math.floor()进行了尝试。它运行了很长时间,可能是一分钟左右,然后当堆栈达到大约10257次调用的深度时,堆栈溢出失败。为了进行比较,标准Math.ceil()在大约5秒内完成。

    这是一个很好的article about JavaScript tail call optimization and related topics。该文章提到的一件事是,您可以使用length = 100000命令行开关和sort( function( a, b ) { return b - a; } )在node.js中获得TCO。

    &#13;
    &#13;
    --harmony_tailcalls
    &#13;
    &#13;
    &#13;