You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在JavaScript中基于call/cc实现维基百科的生成器示例?

如何在JavaScript中基于call/cc实现维基百科的生成器示例?

嗨,我来帮你搞定这个问题!虽然ES6规范里支持Proper Tail Call(PTC,真尾调用),但可惜大部分浏览器都没实现它,不过咱们照样能用闭包和续传风格(CPS)的技巧来实现call/cc,进而复刻维基百科上那个生成器例子。

首先咱们先确定call/cc的简化实现,用续传风格来写:

const callcc = (f, cc) => { f(cc, cc) }

这个函数的作用就是把当前的续传(cc)传给目标函数f,让f能捕获并复用这个续传逻辑。

接下来得写一个适配续传风格的forEach函数,因为要在续传逻辑里逐个遍历列表元素:

const forEach = (f, lst, cc) => {
  const partialForEach = (f, lst, start, cc) => {
    if (start === lst.length) {
      cc();
    } else {
      f(lst[start], () => partialForEach(f, lst, start + 1, cc));
    }
  }
  partialForEach(f, lst, 0, cc)
};

这个forEach会一步步遍历列表,每处理完一个元素就调用传入的续传函数,继续推进遍历流程。

然后就是核心的生成器构造函数generate_one_element_a_time,逻辑完全对应维基百科里的Scheme代码:

const generate_one_element_a_time = lst => {
  let control_state = (ret) => {
    forEach(
      (element, cc) => {
        callcc(
          (resume_here) => {
            // 捕获当前续传,把control_state替换成这个续传,下次调用就从这里继续
            control_state = resume_here
            // 返回当前元素给调用者
            ret(element)
          },
          c => {
            // 更新ret为新的续传,继续遍历下一个元素
            ret = c
            cc()
          }
        )
      },
      lst,
      // 遍历完所有元素后,返回结束标记
      () => ret("you-fell-off-the-end")
    )
  }

  // 生成器函数,每次调用都会触发callcc进入control_state逻辑
  let generator = (cc) => {
    callcc(control_state, cc)
  }

  return generator
}

这里的关键是control_state会被callcc捕获的续传替换,每次调用生成器时,都会从上次暂停的地方继续执行,逐个返回列表里的元素,直到所有元素都返回完,就输出结束标记。

最后咱们来测试这个生成器:

const generate_digit = generate_one_element_a_time([0,1,2])

generate_digit(console.log) // 输出 0
generate_digit(console.log) // 输出 1
generate_digit(console.log) // 输出 2
generate_digit(console.log) // 输出 you-fell-off-the-end

每次调用generate_digit(console.log),都会从上一次暂停的位置继续,依次返回列表里的元素,直到遍历结束。

备注:内容来源于stack exchange,提问作者CidTori

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 15:27:36