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
随时随地看视频