[JS] generator版的陰陽謎題
1. 背景
阴阳谜题(yin yang puzzle),指的是以下Scheme代码实现的无限循环,
(let* [(yin ((lambda (foo) (newline) foo) (call/cc (lambda (bar) bar)))) (yang ((lambda (foo) (display "*") foo) (call/cc (lambda (bar) bar))))] (yin yang))
输出结果为,
* ** *** **** ***** ****** ******* ...
2. Python实现的阴阳谜题
今天在知乎看到了,
阴阳谜题的Python实现,就顺势把它翻译成了JS版。
def puzzle(): def yin(yin): yield '@' def yang(yang): yield '*' yield from yin(yang) yield from yang(yang) yield from yin(yin)for x, _ in zip(puzzle(), range(256)): print(x, end='') print()
3. JavaScript
function* solution() { function* yin(_yin) { yield '@'; function* yang(_yang) { yield '*'; yield* _yin(_yang); } yield* yang(yang); } yield* yin(yin); }const iter = solution();console.log(iter.next()); // @console.log(iter.next()); // *console.log(iter.next()); // @console.log(iter.next()); // *console.log(iter.next()); // *console.log(iter.next()); // @console.log(iter.next()); // *console.log(iter.next()); // *console.log(iter.next()); // *console.log(iter.next()); // @
4. 原理分析
function* solution() { function* yin(_yin) { // 2. yin=yin1, _yin=yin1 // 8. yin=yin1, _yin=yang1 // 17. yin=yin1, _yin=yang2 // 29. yin=yin1, _yin=yang3 // 44. yin=yin1, _yin=yang4 yield '@'; // 3. total output: @ // 9. total output: @*@ // 18. total output: @*@**@ // 30. total output: @*@**@***@ // 45. total output: @*@**@***@****@ function* yang(_yang) { // 5. yang=yang1, _yang=yang1, (_yin=yin1) // 11. yang=yang2, _yang=yang2, (_yin=yang1) // 14. yang=yang1, _yang=yang2, (_yin=yin1) // 20. yang=yang3, _yang=yang3, (_yin=yang2) // 23. yang=yang2, _yang=yang3, (_yin=yang1) // 26. yang=yang1, _yang=yang3, (_yin=yin1) // 32. yang=yang4, _yang=yang4, (_yin=yang3) // 35. yang=yang3, _yang=yang4, (_yin=yang2) // 38. yang=yang2, _yang=yang4, (_yin=yang1) // 41. yang=yang1, _yang=yang4, (_yin=yin1) // 47. yang=yang5, _yang=yang5, (_yin=yang4) // 50. yang=yang4, _yang=yang5, (_yin=yang3) // 53. yang=yang3, _yang=yang5, (_yin=yang2) yield '*'; // 6. total output: @* // 12. total output: @*@* // 15. total output: @*@** // 21. total output: @*@**@* // 24. total output: @*@**@** // 27. total output: @*@**@*** // 33. total output: @*@**@***@* // 36. total output: @*@**@***@** // 39. total output: @*@**@***@*** // 42. total output: @*@**@***@**** // 48. total output: @*@**@***@****@* // 51. total output: @*@**@***@****@** // 54. total output: @*@**@***@****@*** yield* _yin(_yang); // 7. _yin=yin1, _yang=yang1 // 13. _yin=yang1, _yang=yang2 // 16. _yin=yin1, _yang=yang2 // 22. _yin=yang2, _yang=yang3 // 25. _yin=yang1, _yang=yang3 // 28. _yin=yin1, _yang=yang3 // 34. _yin=yang3, _yang=yang4 // 37. _yin=yang2, _yang=yang4 // 40. _yin=yang1, _yang=yang4 // 43. _yin=yin1, _yang=yang4 // 49. _yin=yang4, _yang=yang5 // 52. _yin=yang3, _yang=yang5 // 55. _yin=yang2, _yang=yang5 } yield* yang(yang); // 4. yang=yang1 // 10. yang=yang2 // 19. yang=yang3 // 31. yang=yang4 // 46. yang=yang5 } yield* yin(yin); // 1. yin=yin1}
以上代码中,我用序号标明了前55
步的执行过程。
注:
(1)每次执行到function* yang(){}
会创建一个新的generator。
以上用yang1
,yang2
,yang3
,yang4
,yang5
,来标记所创建的不同generator。
(2)由于JS遵循词法作用域规则(闭包),
使用yield*
调用一个generator,进入函数上下文后,
函数内部自由变量的绑定,是该generator被创建时,词法环境中的值。
例如,第4
步yield* yang(yang);
,此时yang=yang1
,
于是第5
步,yang=yang1, _yang=yang1
,
表示调用了yang1
函数,参数_yang
绑定为yang1
。
这时词法环境中_yin=yin1
,所以,我们把它放到了后面的小括号中,写为,5. yang=yang1, _yang=yang1, (_yin=yin1)
。
等到第13
步yield* yang(yang);
,此时_yin=yang1, _yang=yang2
,
于是第14
步,yang=yang1, _yang=yang2
,
表示再次调用了yang1
函数,参数_yang
绑定为yang2
。
这时词法环境中_yin
的值,还是yang1
创建时的_yin
值,
我们找到了第5
步的记录,5. yang=yang1, _yang=yang1, (_yin=yin1)
,
得知,_yin=yin1
,
因此,14. yang=yang1, _yang=yang2, (_yin=yin1)
。
(3)由于yang
函数内部,_yin
的词法绑定总是会发生变化,
因此,yang
函数实际上相当于在进行循环执行。例如,
// 32. yang=yang4, _yang=yang4, (_yin=yang3)// 35. yang=yang3, _yang=yang4, (_yin=yang2)// 38. yang=yang2, _yang=yang4, (_yin=yang1)// 41. yang=yang1, _yang=yang4, (_yin=yin1)
以上代码,相当于利用词法绑定,实现了循环操作。
作者:何幻
链接:https://www.jianshu.com/p/9a5f347a2d65
共同學習,寫下你的評論
評論加載中...
作者其他優質文章