15_22.Generator之next()、throw()、return()

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>

<body>
    <script>
        /*  六、
            Generator 函数的语法之 next()、throw()、return()

            next()、throw()、return()这三个方法本质上是同一件事,可以放在一起理解。
            它们的作用都是让 Generator函数恢复执行,并且使用不同的语句替换 yield表达式。
        */


        //(1)、 next()是将yield 表达式替换成一个值。
        const g = function* (x, y) {
            let result = yield x + y;
            return result;
        }

        const gen = g(1, 2);
        // console.log(gen.next()); // {value: 3, done: false}  
        // console.log(gen.next(1)) // {value: 1, done: true}    
        /* 解析:next没参数时,(yield x+y) = undefined; next参数为1时,(yield x+y) = 1,result = 1; 
                相当于将 “ let result = yield x + y ” 替换成 “ let result = 1 ”                 
        */


        //(2)、 throw()是将yield 表达式替换成一个throw语句。
        // console.log(gen.throw(new Error('出错了')))   // Uncaught Error: 出错了
        /* 解析:相当于将 “ let result = yield x + y ” 替换成 “ let result = throw(new Error('出错了')) ”
        */


        //(3)、 return()是将yield 表达式替换成一个return语句。
        console.log(gen.return()) // {value: 2, done: true}
        /* 解析:如果传参数2,(yield x+y) = return 2,如果没传参数,(yield x+y) = return undefined;
                相当于将 “ let result = yield x + y ” 替换成 “ let result = return 2 ”
        */



        /*(4)、 Generator 函数的语法之 yield* 表达式
             如果在 Generator函数内部,调用另一个 Generator 函数,默认情况下是没有效果的。

             所以 yield* 表达式,用来在一个 Generator函数里面执行另一个 Generator 函数。
        */

        // 例1、
        function* foo() {
            yield 'a';
            yield 'b';
        }

        function* bar() {
            yield 'x';
            foo();
            yield 'y';
        }

        for (let v of bar()) {
            console.log(v)
        }

        // x
        // y

        // 例2、
        function* foo() {
            yield 'a';
            yield 'b';
        }

        function* bar() {
            yield 'x';
            yield* foo();
            yield 'y';
        }

        /*  等同于:  
            function* bar() {
                yield 'x';
                yield 'a';
                yield 'b';
                yield 'y';
            }
        */

        /*  等同于:  
            function* bar() {
                yield 'x';
                for (let v of foo()) {
                    yield v;
                }
                yield 'y';
            }
        */

        for (let v of bar()) {
            console.log(v)
        }

        // "x"
        // "a"
        // "b"
        // "y"


        // 例3、yield* 命令可以很方便地取出嵌套数组的所有成员。
        function* iterTree(tree) {
            if (Array.isArray(tree)) {
                for (let i = 0; i < tree.length; i++) {
                    yield* iterTree(tree[i])
                }
            } else {
                yield tree
            }
        }

        const tree = ['a', ['b', 'c'], ['d', 'e']];

        for (let x of iterTree(tree)) {
            console.log(x)
        }

        // a
        // b
        // c
        // d
        // e


        // 例4、稍微复杂的例子,使用 yield* 语句遍历完全二叉树。

        // 下面是二叉树的构造函数,三个参数分别是左树、当前节点和右树
        function Tree(left, label, right) {
            this.left = left;
            this.label = label;
            this.right = right;
        }

        // x下面是中序(inorder)遍历函数。
        // 由于返回的是一个遍历器,所以要用 generator函数。
        // 函数体内采用递归算法,所以左树和右树要用 yield* 遍历
        function* inorder(t) {
            if (t) {
                yield* inorder(t.left);
                yield t.label;
                yield* inorder(t.right);
            }
        }

        // 下面生成二叉树
        function make(array) {
            // 判断是否为叶节点
            if (array.length == 1) {
                return new Tree(null, array[0], null);
            } else {
                return new Tree(make(array[0]), array[1], make(array[2]));
            }
        }
        let treeList = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]);

        // 遍历二叉树
        var result = [];
        for (let node of inorder(treeList)) {
            result.push(node);
        }

        console.log(result)  //["a", "b", "c", "d", "e", "f", "g"]



        /*(5)、作为对象属性的 Generator 函数:

            如果一个对象的属性是 Generator 函数,可以简写成下面的形式。       
        */
        let obj = {
            *myGeneratorMethod() {
                // ...
            }
        }

        /* 等价于:*/

        let obj = {
            myGeneratorMethod: function* () {
                // ...
            }
        }


        /*(6)、Generator 函数的 this:

            Generator 函数总是返回一个遍历器,es6 规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数的 prototype对象上的方法。        
        */

        function* g() { }
        g.prototype.hello = function () {
            return 'hi!';
        }

        let obj = g();

        console.log(obj instanceof g);  // true
        console.log(obj.hello());      // hi!


        /*(7)、 Generator 与状态机:

            Generator是实现状态机的最佳结构。比如,下面的 clock函数就是一个状态机。        
        */
        var ticking = true;
        var clock = function () {
            if (ticking) {
                console.log('Tick!');
            } else {
                console.log('Tock!');
                ticking = !ticking;
            }
        }

        // 上面代码的 clock函数一共两种状态(Tick和 Tock),每运行一次,就改变一次状态。这个函数如果用 Generator 实现,就是下面这样:
        var clock = function* () {
            while (true) {
                console.log('Tick');
                yield;
                console.log('Tock');
                yield;
            }
        }

        // 上面的 Generator 实现与 es5 实现对比,可以看到少了用来保存状态的外部变量ticking,
        // 这样就更简洁,更安全(状态不会非法篡改)、更符合函数式编程的思想,在写法上也更优雅。
        // Generator之所以可以不用外部变量保存状态,是因为它本身就包含了一个状态信息,即目前是否处于暂停态。


        /*(8)、 Generator 与上下文:

            javascript代码运行时,会产生一个全局的上下文环境,包含了当前所有的变量和对象。
            然后,执行函数(或块级代码)的时候,又会在当前上下文环境的上层,产生一个函数运行的上下文,
            变成当前(active)的上下文由此形成一个上下文环境的堆栈。

            这个堆栈是“后进先出”的数据结构,最后产生的上下文环境首先执行完成,退出堆栈,然后再执行完成
            它下层的上下文,直至所有代码执行完成,堆栈清空。

            Generator 函数不是这样,它执行产生的上下文环境,一旦遇到yield命令,就会暂时退出堆栈,
            但是并不消失,里面的所有变量和对象会冻结在当前状态。等到对它执行 next命令时,
            这个上下文环境又会重新加入调用栈,冻结的变量和对象恢复执行。     
        */

        function* gen() {
            yield 1;
            return 2;
        }
        let g_ = gen();
        console.log(g_.next().value, g_.next().value, );  // 1 2

        /*  上面代码中,第一次执行 g.next()时,Generator函数 gen的上下文会加入堆栈,
            即开始运行 gen内部的代码。
            等遇到yield 1 时,gen上下文退出堆栈,内部状态冻结。
            第二次执行 g.next() 时,gen上下文重新加入堆栈,变成当前的上下文,重新恢复执行。        
        */


        /*(9)、 应用

            Generator 可以暂停函数执行,返回任意表达式的值。这种特点使得 Generator有多钟应用场景。
        */

        /*(9.1)、异步操作的同步化表达
            
            Generator函数的暂停执行的效果,意味着可以把异步操作写在yield表达式里面,
            等到调用next方法时再往后执行。
            这实际上等同于不需要写回调函数了,因为异步操作的后续操作可以放在 yield表达式下面,
            反正要等到调用 next方法时再执行。
            所以,Generator函数的一个重要实际意义就是用来处理异步操作,改写回调函数。        
        */


        // 例1、
        function* loadUI() {
            showLoadingScreen();
            yield loadUIDataAsynchronously();
            hideLoadingScreen();
        }

        var loader = loadUI();
        // 加载 UI
        loader.next();

        // 卸载 UI
        loader.next();

        /*  上面代码中,第一次调用 loadUI 函数时,该函数不会执行,仅返回一个遍历器。
            下一次对该遍历器调用 next方法,显示 Loading 界面,并且异步加载数据。
            等到异步加载完成,再一次使用 next方法,则会隐藏 Loading 界面。
            可以看到,这种写法的好处是所有 Loading 界面的逻辑,都被封装在一个函数,按部就班非常清晰。
        */

        // 例2、Ajax是典型的异步操作,通过 Generator函数部署 Ajax操作,可以用同步的方式表达。
        function* main() {
            var result = yield request('http://some.url');
            var response = JSON.parse(result);
            console.log(response.value)
        }

        function request(url) {
            makeAjaxCall(url, function (response) {
                it.next(response);
            })
        }

        var it = main();
        it.next();

        /*  上面代码的 main函数,就是通过 Ajax 操作获取数据。
            可以看到,除了多了一个 yield,它几乎与同步操作的写法完全一样。
            注意,makeAjaxCall 函数中的 next 方法,必须加上 response参数,因为yield表达式,本身是没有值的,总是等于undefined.
        */

        // 例3、通过 Generator函数逐行读取文本文件。
        function* numbers() {
            let file = new FileReader('numbers.txt');
            try {
                while (!file.eof) {
                    yield parseInt(file.readLine(), 10);
                }
            } finally {
                file.close();
            }
        }
        // 上面代码打开文本文件,使用 yield表达式可以手动逐行读取文件。


        /*(9.2)、控制流管理

                如果有一个多步操作非常耗时,采用回调函数,可能会写成下面这样。
        */

        step1(function (value1) {
            step2(value1, function (value2) {
                step3(value2, function (value3) {
                    step4(value3, function (value4) {
                        // ...
                    })
                })
            })
        })

        // 采用 Promise 改写上面的代码:
        Promise.resolve(step1)
            .then(step2)
            .then(step3)
            .then(step4)
            .then(function (value4) {
                // ...
            }, function (error) {
                // ...
            })
            .done();

        // 上面代码已经把回调函数,改成了直线执行的形式,但是加入了大量Promise的语法。
        // Generator 函数可以进一步改善代码运行流程。

        function* longRunningTask(value1) {
            try {
                var value2 = yield step1(value1);
                var value3 = yield step2(value2);
                var value4 = yield step3(value3);
                var value5 = yield step4(value4);
                // Do something with value4
            } catch (e) {
                // Handle any error from step1 through step4
            }
        }

        // 然后,使用一个函数,按次序自动执行所有步骤。
        scheduler(longRunningTask(initialValue));

        function scheduler(task) {
            var taskObj = task.next(task.value);
            // 如果 Generator 函数未结束,就继续调用。
            if (!taskObj.done) {
                task.value = taskObj.value;
                scheduler(task);
            }
        }


    </script>
</body>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
def train_step(real_ecg, dim): noise = tf.random.normal(dim) for i in range(disc_steps): with tf.GradientTape() as disc_tape: generated_ecg = generator(noise, training=True) real_output = discriminator(real_ecg, training=True) fake_output = discriminator(generated_ecg, training=True) disc_loss = discriminator_loss(real_output, fake_output) gradients_of_discriminator = disc_tape.gradient(disc_loss, discriminator.trainable_variables) discriminator_optimizer.apply_gradients(zip(gradients_of_discriminator, discriminator.trainable_variables)) ### for tensorboard ### disc_losses.update_state(disc_loss) fake_disc_accuracy.update_state(tf.zeros_like(fake_output), fake_output) real_disc_accuracy.update_state(tf.ones_like(real_output), real_output) ####################### with tf.GradientTape() as gen_tape: generated_ecg = generator(noise, training=True) fake_output = discriminator(generated_ecg, training=True) gen_loss = generator_loss(fake_output) gradients_of_generator = gen_tape.gradient(gen_loss, generator.trainable_variables) generator_optimizer.apply_gradients(zip(gradients_of_generator, generator.trainable_variables)) ### for tensorboard ### gen_losses.update_state(gen_loss) ####################### def train(dataset, epochs, dim): for epoch in tqdm(range(epochs)): for batch in dataset: train_step(batch, dim) disc_losses_list.append(disc_losses.result().numpy()) gen_losses_list.append(gen_losses.result().numpy()) fake_disc_accuracy_list.append(fake_disc_accuracy.result().numpy()) real_disc_accuracy_list.append(real_disc_accuracy.result().numpy()) ### for tensorboard ### # with disc_summary_writer.as_default(): # tf.summary.scalar('loss', disc_losses.result(), step=epoch) # tf.summary.scalar('fake_accuracy', fake_disc_accuracy.result(), step=epoch) # tf.summary.scalar('real_accuracy', real_disc_accuracy.result(), step=epoch) # with gen_summary_writer.as_default(): # tf.summary.scalar('loss', gen_losses.result(), step=epoch) disc_losses.reset_states() gen_losses.reset_states() fake_disc_accuracy.reset_states() real_disc_accuracy.reset_states() ####################### # Save the model every 5 epochs # if (epoch + 1) % 5 == 0: # generate_and_save_ecg(generator, epochs, seed, False) # checkpoint.save(file_prefix = checkpoint_prefix) # Generate after the final epoch display.clear_output(wait=True) generate_and_save_ecg(generator, epochs, seed, False)
06-08

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值