【问题标题】:call/CC with closures带闭包的呼叫/CC
【发布时间】:2012-01-07 02:26:55
【问题描述】:

Wikipedia 提到“在任何支持闭包和适当尾调用的语言中,都可以编写连续传递风格的程序并手动实现 call/cc。”

如何在 javascript 中实现此功能?我知道javascript不做tco,但假设堆栈空间没有用完

【问题讨论】:

  • 查看jwacsthis 以获得简单的机制。
  • 第二个例子解释了延续,你可以用 call/cc 实现异常,但不去实现 call/cc。第一个示例缺少文档(至少乍一看),并且破译源代码有点矫枉过正:)

标签: javascript callcc


【解决方案1】:

有可能

https://github.com/zaoqi/callcc.js/blob/master/callcc.js

async function callcc(f){
  return await new Promise((resolve,reject)=>{
    const resolve_packed=(v)=>{
      resolve(v)
      return new Promise((resolve,reject)=>{})
    }
    f(resolve_packed).then(resolve).catch(reject)
  })
}

使用它:

test('test1',()=>{
  expect.assertions(1)
  expect((()=>{
    async function q(k,v){
      console.log('testing1')
      await k(v)
    }
    return callcc(async function(k){
      console.log('testing0')
      await q(k,133)
      console.error('test error')
    })
  })()).resolves.toBe(133)
})

【讨论】:

  • async function callcc(f) { return await new Promise(...) } 等同于async function callcc(f) { return new Promise(...) } 等同于function callcc(f) { return new Promise(...) } 等同于callcc = f => new Promise(...)
【解决方案2】:

是的,这是可能的。请参阅此question。这就是你将如何实现它:

Function.prototype.async = async;

function async() {
    setTimeout.bind(null, this, 0).apply(null, arguments);
}

function callcc(f, cc) {
    f.async(cc);
}

那么你可以这样使用它:

pythagoras.async(3, 4, alert);

function pythagoras(x, y, cont) {
    callcc.async(square.bind(null, x), function cc(x_squared) {
        callcc.async(square.bind(null, y), function cc(y_squared) {
            add.async(x_squared, y_squared, cont);
        });
    });
}

function square(x, cont) {
    multiply.async(x, x, cont);
}

function multiply(x, y, cont) {
    cont.async(x * y);
}

function add(x, y, cont) {
    cont.async(x + y);
}

您可以在这里摆弄演示:http://jsfiddle.net/brZrd/

【讨论】:

  • 这个答案似乎只是你更完整的答案stackoverflow.com/a/14022348/2255628的结论实际上这里的所有代码似乎都是有效的精确复制那里的最终代码,以避免重复任何代码或答案文字,恕我直言,这个答案应该只是链接/指向那里并告诉那里如何回答这个问题,+移动到那个主要位置 jsfiddle.net/brZrd 在那里丢失。同样,我会将我对此的所有相关回复都放在那里,并鼓励其他人也这样做。 --所有这些都是为了巩固和避免冗余和混乱。
  • @DestinyArchitect 复制并不是一件坏事。 StackOverflow 的创建者之一 Jeff Atwood 支持复制:blog.stackoverflow.com/2010/11/…。现在,我的答案确实是早期答案的副本。但是,这并非毫无意义,因为 a)它是对早期答案要点的总结,并带有一个链接以获得更详细的解释 b)它解决了 OP 的问题,并且本身就是一个完整的答案。如果我只是链接到我之前的答案而不进行总结,那么它就不是一个真正的答案。 =)
【解决方案3】:

无法在 JavaScript 中编写 call/cc 实现:

JavaScript 不满足“正确的尾调用”的要求(没有额外的堆栈创建)。但是,我相信使用异常的 form 延续是可能的,例如在 Jetty 中发现的那种。 “CPS”就像传递一个函数对象一样简单,尽管最终它也会遇到堆栈问题,除非偶尔退出。

编码愉快。

【讨论】:

  • 是否可以在 JS 中通过(偶尔)使用 setTimeout() 清除堆栈来做到这一点?
  • @StevenLu “有点”。这就是我所暗示的“除非偶尔退出”——也许是捕捉到溢出异常?虽然我不确定如何清除实现..
  • 是的。对我来说,我看不出有任何理由证明在 CPS 中编写 JS 是合理的,除非该任务是高度异步的,并且需要或极大地受益于存储或传递后续函数以在以后运行:如果不是这种情况,或者如果性能很重要,常规的递归或迭代实现可能更合适。这应该意味着由 setTimeout(展开堆栈)引起的短暂延迟是可以容忍的。至于将异常加入其中……听起来有点吓人。
  • 函数调用cc(f,cc) { f(function(x,k) { cc(x) },cc) } 如图matt.might.net/articles/by-example-continuation-passing-style
  • 我还看到人们使用 Cheney-on-the-MTA 方法,使用 Sugar.js 创建每一步的 thunk,并在堆栈溢出时从新堆栈开始。它相当慢,但确实有效。
猜你喜欢
  • 2021-03-26
  • 1970-01-01
  • 2012-06-30
  • 1970-01-01
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 1970-01-01
  • 2021-02-28
相关资源
最近更新 更多