如何在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
相关产品推荐
相关产品推荐

