当前位置: 首页 > 面试题库 >

Node.js尾部调用优化:可能吗?

叶越
2023-03-14
问题内容

到目前为止,我一直喜欢JavaScript,并决定使用Node.js作为我的引擎,它声称Node.js提供了TCO。但是,当我尝试使用Node.js运行此(显然是尾部调用)代码时,会导致堆栈溢出:

function foo(x) {
    if (x == 1) {
        return 1;
    }
    else {
        return foo(x-1);
    }
}

foo(100000);

现在,我做了一些挖掘,发现了这一点。在这里,看来我应该这样写:

function* foo(x) {
    if (x == 1) {
        return 1;
    }
    else {
        yield foo(x-1);
    }
}

foo(100000);

但是,这给了我语法错误。我试过它的各种排列,但在所有的情况下,Node.js的似乎不满 的东西

本质上,我想了解以下内容:

  1. Node.js是否执行TCO?
  2. 这个神奇的yield东西如何在Node.js中工作?

问题答案:

这里有两个相当不同的问题:

  • Node.js是否执行TCO?
  • 这个神奇的yield事物如何在Node.js中工作?

Node.js是否执行TCO?

TL; DR从Node 8.x开始不再可用。它做了一段时间了,一个或另一个,但是在撰写本文时(2017年11月),它不再存在,因为它使用的底层V8JavaScript引擎不再支持TCO。。

细节:

尾呼叫优化(TCO)是ES2015(“ES6”)规范的必需部分。因此,直接支持它不是NodeJS,而是NodeJS使用的V8 JavaScript引擎需要支持的东西。

从Node8.x开始,V8不支持TCO,甚至不支持标记。它可能会在将来的某个时候再次出现。。

节点7.10至少降至6.5.0(我的注释说为6.2,但node.green表示不同意)仅在严格模式下(--harmony在6.6.0及更高--harmony_tailcalls版本中)标记后面支持TCO

如果要检查安装,请使用node.green使用的测试(如果使用的是相关版本,请确保使用该标志):

function direct() {
    "use strict";
    return (function f(n){
      if (n <= 0) {
        return  "foo";
      }
      return f(n - 1);
    }(1e6)) === "foo";
}

function mutual() {
    "use strict";
    function f(n){
      if (n <= 0) {
        return  "foo";
      }
      return g(n - 1);
    }
    function g(n){
      if (n <= 0) {
        return  "bar";
      }
      return f(n - 1);
    }
    return f(1e6) === "foo" && f(1e6+1) === "bar";
}

console.log(direct());
console.log(mutual());



$#仅适用于某些版本的Node,尤其不是8.x或(当前)9.x;往上看
$ node --harmony tco.js
真正
真正

这个神奇的yield东西如何在Node.js中工作?

这是ES2015的另一件事(“生成器功能”),因此也是V8必须实现的东西。它已在Node
6.6.0中的V8版本中完全实现(并且已经有多个版本),并且没有任何标志。

生成器函数(使用编写function*和使用的函数yield)能够停止并返回捕获其状态的迭代器,并可以在以后的情况下继续其状态,从而发挥作用。AlexRauschmeyer在这里对它们进行了深入的介绍。

这是一个使用显式生成器函数返回的迭代器的示例,但是您通常不会这样做,稍后我们将看到原因:

"use strict";
function* counter(from, to) {
    let n = from;
    do {
        yield n;
    }
    while (++n < to);
}

let it = counter(0, 5);
for (let state = it.next(); !state.done; state = it.next()) {
    console.log(state.value);
}

具有以下输出:

0
1个
2
3
4

运作方式如下:

  • 当我们调用counterlet it = counter(0, 5);)时,对的初始内部状态counter已初始化,我们立即获得了一个迭代器。counter尚未运行任何实际代码(尚未)。
  • 调用通过第一条语句向上it.next()运行代码。此时,暂停并存储其内部状态。返回带有标志和的状态对象。如果标志为,则为语句产生的值。counter``yield``counter``it.next()``done``value``done``false``value``yield
  • 每次调用都会it.next()将状态转发counter到下一个状态yield
  • 当一个呼叫it.next()品牌counter完成并返回时,状态对象,我们取回已done设置truevalue设置的返回值counter

拥有迭代器和状态对象的变量以及调用it.next()和访问doneandvalue属性都是(通常)阻碍我们通常试图做的事情的样板,因此ES2015提供了一条新for-of语句来将其全部删除我们,并给我们每个价值。这是上面用编写的相同代码for-of

"use strict";
function* counter(from, to) {
    let n = from;
    do {
        yield n;
    }
    while (++n < to);
}

for (let v of counter(0, 5)) {
    console.log(v);
}

v对应state.value于我们前面的示例,它完成了for-of所有的it.next()调用并done为我们进行检查。



 类似资料:
  • 问题内容: 我尝试在网络上进行挖掘以使问题得到解答。我找到了一些与达芬奇计划有关的文件。这被标记为与在JVM中包含闭包有关的JSR 292。这个项目实现了吗,它是Java 8的一部分吗? 问题答案: 据我所知,Java 8没有尾调用优化。Afaik与实际的编译器技巧无关,因为它很简单,但是为了安全起见保留了一个调用栈。但是我想使用字节码重写器是可能的。

  • 问题内容: 从Java 8开始,Java不提供尾叫优化(TCO)。经过研究,我知道原因是: 在JDK类中,有许多对安全性敏感的方法,它们依赖于对JDK库代码和调用代码之间的堆栈帧进行计数,以确定谁在调用它们。 但是,基于JVM的Scala支持尾叫优化。Scala在编译时进行尾递归优化。Java为什么不能使用相同的方法? PS:不确定Java的最新版本(截至现在的Java 11)是否具有TCO。如果

  • 尾部调用优化 (TCO) 正如我们早前简单提到的,ES6包含了一个冒险进入性能世界的具体需求。它是关于在函数调用时可能会发生的一种具体的优化形式:尾部调用优化(TCO)。 简单地说,一个“尾部调用”是一个出现在另一个函数“尾部”的函数调用,于是在这个调用完成后,就没有其他的事情要做了(除了也许要返回结果值)。 例如,这是一个带有尾部调用的非递归形式: function foo(x) { r

  • 尾部调用优化对返回void的函数的递归调用有效吗?例如,我有一个函数,void fun() 在这里,编译器不会知道,调用fun()是最后一条语句。那么尾部调用优化是否只针对返回某些值的函数?

  • 我有以下代码失败,错误如下: RuntimeError:超出最大递归深度 我试图重写它以允许尾部递归优化(TCO)。我相信,如果发生了TCO,那么这段代码应该是成功的。 我应该得出结论,Python不做任何类型的TCO,还是我只需要以不同的方式定义它?

  • Node.js 调优 作为一门后端语言,肯定要求运行的效率最优化,以实现对于资源最小损耗,这一章正是围绕这个话题展开。调优是一个有挑战性的活儿,可能经常让人摸不着头脑,下面的内容尽量使用实例来带入一个个调优的场景。 11.1 准备工作 首先我们准备一个 http server 端代码,请求后返回一个二维码图片: var http = require('http'); var ccap = requ