首页 > 其他分享 >迭代器和生成器

迭代器和生成器

时间:2023-06-25 19:34:21浏览次数:33  
标签:const 迭代 生成器 yield next current

迭代器和生成器_生成器

根据许多平台(例如 GitHub),JavaScript 是目前最流行的编程语言。然而,流行就等于是最先进或最受喜爱的语言吗?它缺少某些被认为是其他语言不可或缺的组成部分的结构,例如广泛的标准库、不变性和宏。但在我看来,有一个细节没有得到足够的重视——发电机。

在本文中,我想解释迭代器和生成器的可能用例,以及它们如何改进代码的冗长性。我希望,在阅读完本文后,下面的代码片段能够理解所有内容:

while (true) {
    const data = yield getNextChunk();
    const processed = processData(data);
    try {
        yield sendProcessedData(processed);
        showOkResult();
    } catch (err) {
        showError();
    }
}


这是系列的第一部分:迭代器和生成器。

迭代器

因此,迭代器是一个提供顺序访问数据的接口。

如您所见,该定义没有提及任何有关数据结构或内存的内容。确实,一个空值序列可以表示为一个迭代器而不占用内存空间。

让我们举几个例子:

当您想到迭代器时,您首先想到的可能是数组。它是一种在内存中存储一系列值的数据结构。它也是一个迭代器,因为它提供对其元素的顺序访问。

const arr = [1, 2, 3];
for (const item of arr) {
    console.log(item);
}


字符串也是如此。它们作为字符序列存储在内存中,并提供对它们的顺序访问。

const str = 'abc';
for (const char of str) {
    console.log(char);
}


让我们看一下下面的函数示例:

const fn = () => Math.random();


这个函数可以被认为是一个迭代器,因为它提供了对随机数的顺序访问。

那么,如果数组(语言中的基本数据结构之一)允许我们按顺序和任意顺序处理数据,那么为什么我们需要迭代器呢?

假设我们需要一个迭代器来实现自然数或斐波那契数列或任何其他无限序列。很难在数组中存储无限序列。我们需要一种机制来逐渐用数据填充数组并删除旧数据,以防止填满进程的整个内存。这种不必要的复杂性增加了额外的实现和维护开销,而无需数组的解决方案只需几行代码即可实现:

const getNaturalRow = () => {
    let current = 0;
    return () => ++current;
};


迭代器还可用于表示从外部通道(例如 WebSocket)检索的数据。

在 JavaScript 中,任何具有 next() 方法的对象都被视为迭代器,该方法返回一个具有值(当前迭代器值)和完成(指示序列结束的标志)的结构。此约定在ECMAScript 语言标准中进行了描述。这样的对象实现了 Iterator 接口。让我们用这种格式重写前面的例子:

const getNaturalRow = () => {
    let current = 0;
    return {
        next: () => ({ value: ++current, done: false }),
    };
};


在 JavaScript 中,还有 Iterable 接口。它表示一个对象,该对象具有返回迭代器的 @@iterator 方法(可通过 Symbol.iterator 常量访问)。可以使用 for..of 循环迭代实现此接口的对象。让我们再次重写我们的示例,这次作为一个 Iterable 实现:

const naturalRowIterator = {
    [Symbol.iterator]: () => ({
        _current: 0,
        next() { return {
            value: ++this._current,
            done: this._current > 3,
       }},
   }),
}

for (num of naturalRowIterator) {
    console.log(num);
}
// output: 1, 2, 3


如您所见,我们必须让标志“完成”在某个时刻发生变化,否则循环将是无限的。

发电机

迭代器发展的下一个阶段是生成器的引入。它们提供语法糖,允许将迭代器的值作为函数的结果返回。function*生成器是用星号声明并返回迭代器的函数。迭代器本身并没有明确返回;相反,该函数使用yield关键字生成迭代器的值。当函数完成执行时,迭代器被视为完成(后续next方法调用将返回{ done: true, value: undefined }.

function* naturalRowGenerator() {
    let current = 1;
    while (current <= 3) {
        yield current;
        current++;
    }
}

for (num of naturalRowGenerator()) {
    console.log(num);
}
// output: 1, 2, 3


即使在这个简单的示例中,生成器的主要细微差别也很明显:生成器函数中的代码不会同步执行next作为相应迭代器上方法调用的结果,生成器代码的执行是增量发生的。让我们使用前面的示例检查生成器代码是如何执行的。我们将使用一个特殊的光标来标记生成器暂停执行的位置。

在调用 naturalRowGenerator 时,会创建一个迭代器。

function* naturalRowGenerator() {
    ▷let current = 1;
    while (current <= 3) {
        yield current;
        current++;
    }
}


接下来,当我们next三次调用该方法时,或者在我们的例子中,遍历循环三次时,光标位于 yield 语句之后。

function* naturalRowGenerator() {
    let current = 1;
    while (current <= 3) {
        yield current; ▷
        current++;
    }
}


并且对于所有后续next调用,以及退出循环后,生成器完成其执行。后续调用的结果next将是{ value: undefined, done: true }

将参数传递给迭代器

假设我们需要添加功能来重置当前计数器并在我们的自然数迭代器中从头开始计数。

naturalRowIterator.next() // 1
naturalRowIterator.next() // 2
naturalRowIterator.next(true) // 1
naturalRowIterator.next() // 2


很清楚如何在自定义迭代器中处理这样的参数,但是生成器呢?
原来生成器是支持参数传递的!

function* naturalRowGenerator() {
    let current = 1;
    while (true) {
        const reset = yield current;
        if (reset) {
          current = 1;
        } else {
          current++;
        }
    }
}


作为 yield 运算符的结果,传递的参数变得可用。让我们尝试使用游标方法来阐明这一点。在创建迭代器的那一刻,什么都没有改变。现在让我们在第一个next方法调用后停止:

function* naturalRowGenerator() {
    let current = 1;
    while (true) {
        const reset = ▷yield current;
        if (reset) {
          current = 1;
        } else {
          current++;
        }
    }
}


从 yield 运算符返回后定位游标。在下一次next调用中,传递给函数的值将设置reset变量的值。但是第一次next调用中传递的值会发生什么变化?它无处可去!如果需要将初始值传递给生成器,可以通过生成器的参数来实现。这是一个例子:

function* naturalRowGenerator(start = 1) {
    let current = start;
    while (true) {
        const reset = yield current;
        if (reset) {
          current = start;
        } else {
          current++;
        }
    }
}

const iterator = naturalRowGenerator(10);
iterator.next() // 10
iterator.next() // 11
iterator.next(true) // 10


结论

我们已经探讨了迭代器的概念及其在 JavaScript 中的实现。此外,我们还了解了生成器,这是一种方便地实现迭代器的语法结构。

尽管在本文中,我提供了带有数字序列的示例,但 JavaScript 中的迭代器可以解决范围广泛的任务。它们可以表示任何数据序列甚至许多有限状态机。在下一篇文章中,我想讨论如何使用生成器来构建异步进程(协同程序、goroutines、CSP 等)。

标签:const,迭代,生成器,yield,next,current
From: https://blog.51cto.com/u_15739596/6547794

相关文章

  • 迭代器模式
    迭代器模式这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式属于行为型模式。思考问题:如何实现顺序访问且不知道集合底层表示?例子:迭代接口packageorg.kouhao.design.patterns.迭代模式;/***@authoradmin*/publicinterfaceIterato......
  • 深度学习/图像处理历史最全最细-网络、技巧、迭代-论文整理分享
        本资源整理了深度学习/图像处理技术发展过程中的所有模型、优化技巧、网络结构优化、迭代过程中所有经典论文,并进行了详细的分类,按重要程度进行了仔细的划分,对于想要了解深度学习模型迭代朋友来说非常值得参考。     本资源整理自网络,源地址:https://github.com/xw-hu/......
  • Python 迭代器和生成器
    Python迭代器和生成器1、迭代器迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于......
  • MongoDB批量导入Redis优化迭代笔记
    背景统计最近五天所有content信息的正文字节数(正文字段占用较多),然后根据这个大小,推送存在redis要配置多少的内存。统计方法1.在mongodb中查询db.content_.aggregate([{$match:{updatetime:{$gte:1686134400000,//对应日期"2023-06-07T00:00:00Z"的......
  • 强化学习从基础到进阶-常见问题和面试必知必答[2]:马尔科夫决策、贝尔曼方程、动态规划
    强化学习从基础到进阶-常见问题和面试必知必答[2]:马尔科夫决策、贝尔曼方程、动态规划、策略价值迭代1.马尔科夫决策核心词汇马尔可夫性质(Markovproperty,MP):如果某一个过程未来的状态与过去的状态无关,只由现在的状态决定,那么其具有马尔可夫性质。换句话说,一个状态的下一个状态......
  • 强化学习从基础到进阶-常见问题和面试必知必答[2]:马尔科夫决策、贝尔曼方程、动态规划
    强化学习从基础到进阶-常见问题和面试必知必答[2]:马尔科夫决策、贝尔曼方程、动态规划、策略价值迭代1.马尔科夫决策核心词汇马尔可夫性质(Markovproperty,MP):如果某一个过程未来的状态与过去的状态无关,只由现在的状态决定,那么其具有马尔可夫性质。换句话说,一个状态的下一个状态......
  • 代码随想录算法训练营第十二天| 递归遍历 (必须掌握)迭代遍历 统一迭代
    递归遍历重点:1,TreeNode的自定义2,val=0== val=NULL;代码:1voidpreRecursor(TreeNode*root,vector<int>&result)2{3if(root==NULL)4return;5result.push_back(root->val);6preRecursor(root->left,result);7......
  • Infinigen矩阵:自然世界的逼真3D场景程序生成器
    你,有没有那么一瞬间,认为我们生活的世界,就是模拟的矩阵世界。而现在,矩阵正式开启。静心感受,这个人类生存已久的地球,大自然的一切,都是虚幻世界。北极冰川太阳升起。海底世界中的千奇百怪的鱼群、五彩斑斓的珊瑚礁。高山飞雪,老鹰在浩瀚无垠的天空中翱翔。炽热沙漠,凶险的蛇自由......
  • 20230426 20. 迭代器模式
    介绍迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式你需要对聚集有多种方式遍历时,可以考虑用迭代器模式。为遍历不同的聚集结构提供......
  • 你的专属音乐生成器「GitHub 热点速览」
    如果你制作视频,一定会碰到配乐的问题。虽然网上找的一些免费配乐能勉强满足需求,但是如果有个专属的配乐生成器,根据你的视频画面生成对应配乐是不是不错呢?audiocraft也许能帮助你,把相关画面用文本描述一下,它就能生成相对应的音乐。当然,本周除了文本生成音乐的audiocraft之外,还......