gpt4 book ai didi

templates - 尼姆 : How to wrap/derive an iterator from another iterator?

转载 作者:行者123 更新时间:2023-12-03 07:57:42 31 4
gpt4 key购买 nike

假设我们有一些 existingIterator迭代任意类型的元素T .我现在想要实现的是从 existingIterator 派生一个新的迭代器具有修改的行为。想想这样的例子:

  • 限制原始迭代器的长度,例如 existingIterator.take(n) .
  • 映射元素,例如 existingIterator.map(modifier)
  • 过滤某些元素,例如 existingIterator.filter(predicate) .

  • 在所有这些情况下,我只想生成另一个迭代器,以便可以执行以下操作:
    for x in existingIterator.filter(something)
    .map(modifier)
    .take(10):
    ...

    我的一般问题是:如何编写一个通用迭代器或模板,它采用现有迭代器并返回修改后的迭代器?

    一个后续问题是为什么标准库中没有这些基本功能——也许我遗漏了一些东西?

    这是我尝试过的:

    尝试 1

    让我们以 take(n)功能为例。我的第一种方法是使用常规泛型 iterator :
    iterator infinite(): int {.closure.} =
    var i = 0
    while true:
    yield i
    inc i

    iterator take[T](it: iterator (): T, numToTake: int): T {.closure.} =
    var i = 0
    for x in it():
    if i < numToTake:
    yield x
    inc i

    for x in infinite.take(10):
    echo x

    这可以编译,但不幸的是,它并没有真正起作用:(1)元素没有正确迭代(它们都只是零,可能是 bug?),(2)看起来我的程序陷入了无限循环, (3) 它仅适用于闭包迭代器,这意味着我不能包装任意迭代器。

    尝试 2

    闭包迭代器的限制表明这个问题实际上需要一个模板解决方案。
    template take[T](it: iterator(): T, numToTake: int): expr {.immediate.} =
    var i = 0
    iterator tmp(): type(it()) =
    for item in it:
    if i < numToTake:
    yield item
    inc i
    tmp

    这几乎似乎工作(即模板编译)。但是,如果我现在调用 for x in infinite.take(10)我得到:
    `Error: type mismatch: got (iterator (): int{.closure, gcsafe, locks: 0.})`

    我试图附加一个 ()实际“调用”迭代器,但它仍然不起作用。所以它归结为一个问题:我应该如何从模板构造/返回迭代器?

    最佳答案

    问题在于

    for x in infinite.take(10):
    echo x

    或者,更具体地说,调用 infinite.take(10) ,我们也可以写成 take(infinite, 10) .不像 Sather , Nim 没有 once它的迭代器的参数,因此没有办法区分每个循环应该评估一次的参数和每个循环迭代应该评估一次的参数。

    在将闭包迭代器作为参数传递给另一个闭包迭代器的情况下,这意味着 infinite 的新实例每次循环时都会创建具有新环境的迭代器。这将使 infinite一次又一次地从零开始。

    内联迭代器通常只会在每个循环中评估一次它们的参数(这是大多数情况下的预期行为)。闭包迭代器必须将其主体转换为状态机,这会改变它们的调用方式。它们也可以以不同的方式使用:特别是,闭包迭代器可以有多个调用点,这与内联迭代器不同;例如 let iter = ...; iter(someArgument); iter(someOtherArgument) .因此,我不确定我们是在查看错误还是此处的预期行为。

    您可以通过不传递 infinite 来解决此问题至 take直接,但使用 let第一的。您的 take 中还有一个错误循环不会终止的代码,您还需要修复它。生成的代码将类似于:
    iterator infinite(): int {.closure.} =
    var i = 0
    while true:
    yield i
    inc i

    iterator take[T](it: iterator (): T, numToTake: int): T {.closure.} =
    var i = 0
    for x in it():
    if i >= numToTake:
    break
    yield x
    inc i

    let inf = infinite
    for x in inf.take(10):
    echo x

    如果你想参数化 infinite ,这可以通过将迭代器包装在模板或过程中来完成,例如:
    template infiniteFrom(x: int): (iterator (): int) =
    (iterator (): int =
    var i = x
    while true:
    yield i
    inc i)

    ...

    let inf = infiniteFrom(1)
    for x in inf.take(10):
    echo x

    关于templates - 尼姆 : How to wrap/derive an iterator from another iterator?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29588345/

    31 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com