【问题标题】:What is a "yield return" equivalent in the D programming language?D 编程语言中的“收益回报”等价物是什么?
【发布时间】:2011-04-20 21:02:18
【问题描述】:

这是一个简单的 C# 生成器。

    IEnumerable<int> Foo()
    {
        int a = 1, b = 1;
        while(true)
        {
            yield return b;
            int temp = a + b;
            a = b;
            b = temp;
        }
    }

如何在 Digital Mars D 中编写类似的生成器?

(问题是关于收益回报语句)

谢谢!


更新。 那很有意思。因为我只是在生成一个数学序列,所以使用recurrence 可能是一个不错的选择。

auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);

foreach (e; take(fib, 10)) // <- prints first ten numbers from the sequence
{ 
    writeln(e); 
}

【问题讨论】:

  • 请注意,您现在应该可以编写 fib.take(10),因为统一函数调用语法已得到增强。

标签: d dmd


【解决方案1】:

std.concurrency 模块现在有一个 Generator 类,这使这变得更加容易(并且您不需要第三方库)。

该类是一个输入范围,因此可以与 for 循环和所有标准 std.range/std.algorithm 函数一起使用。

import std.stdio;
import std.range;
import std.algorithm;
import std.concurrency : Generator, yield;

void main(string[] args) {
    auto gen = new Generator!int({
        foreach(i; 1..10)
            yield(i);
    });

    gen
        .map!(x => x*2)
        .each!writeln
    ;
}

【讨论】:

  • 我很确定Generator 模板在提出问题时不存在...
【解决方案2】:

here;示例摘录如下:

module main;

import std.stdio;
import generators;

void genSquares(out int result, int from, int to)
{
    foreach (x; from .. to + 1)
        yield!result(x * x);
}

void main(string[] argv)
{
    foreach (sqr; generator(&genSquares, 10, 20))
        writeln(sqr);
}

【讨论】:

    【解决方案3】:

    在 D 中没有精确的等价物。以下是一些粗略的等价物:

    使用 opApply 风格的内部迭代。但是,这不允许以同步方式迭代两个迭代器:

    struct Foo {
        int opApply(int delegate(ref int) dg) {
            int a = 1, b = 1;
            int result;
            while(true) {
                result = dg(b);
                if(result) break;
                int temp = a + b;
                a = b;
                b = temp;
            }
    
            return result;
        }
    }
    
    void main() {
        // Show usage:
        Foo foo;
        foreach(elem; foo) {
            // Do stuff.
        }
    }
    

    使用范围。在某些情况下,它们稍微难以编写,但非常有效并且允许锁步迭代。这也可以通过foreach 循环进行迭代,就像opApply 版本一样:

    struct Foo {
        int a = 1, b = 1;
    
        int front() @property {
            return b;
        }
    
        void popFront() {
            int temp = a + b;
            a = b;
            b = temp;
        }
    
        // This range is infinite, i.e. never empty.
        enum bool empty = false;
    
        typeof(this) save() @property { return this; }
    }
    

    如果您真的需要协程风格的东西,您可以使用 core.thread.Fiber 将 range 和 opApply 组合在一起,但您可能会发现 range 或 opApply 几乎一直都能满足您的需求。

    【讨论】:

    • 虽然 D 版本有点冗长,但它至少更具可读性:我无法理解 Foo 每次都应该返回 1(一开始就为 b 分配了 1) .此外,每种语言的处理方式都略有不同,例如 Ruby 基本上是 opApply 版本,没有结构开销。
    • @he_the_great:另一个权衡是两个 D 版本都更高效。虽然可以使用纤维模拟协程/生成器样式迭代,但我认为它的漂亮语法被故意省略了,因为每次迭代需要花费大约一百个循环上下文切换。 opApply 版本的委托调用可能需要 5 或 10 个周期,与手动编码版本相比,范围版本不需要任何成本,因为所有范围原语都符合内联条件。这两种风格中的一种可以在大约 99.9% 的情况下满足您的需要。
    • 谢谢,dsimcha。使用范围看起来很有希望,我会查一下。
    猜你喜欢
    • 1970-01-01
    • 2011-02-24
    • 2014-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-29
    • 2020-06-21
    • 1970-01-01
    相关资源
    最近更新 更多