亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

[JS] generator版的陰陽謎題

標簽:
JavaScript

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。
以上用yang1yang2yang3yang4yang5,来标记所创建的不同generator。

(2)由于JS遵循词法作用域规则(闭包),
使用yield*调用一个generator,进入函数上下文后,
函数内部自由变量的绑定,是该generator被创建时,词法环境中的值。

例如,第4yield* yang(yang);,此时yang=yang1
于是第5步,yang=yang1, _yang=yang1
表示调用了yang1函数,参数_yang绑定为yang1

这时词法环境中_yin=yin1,所以,我们把它放到了后面的小括号中,写为,
5. yang=yang1, _yang=yang1, (_yin=yin1)

等到第13yield* 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


點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消