【问题标题】:Recursive generator without explicit enumerators没有显式枚举器的递归生成器
【发布时间】:2017-10-25 14:33:46
【问题描述】:

这是一个使用显式枚举器的生成器:

static IEnumerable<string> generate(string s)
{
    yield return new Regex("e").Replace(s, "", 1);

    yield return new Regex("aaaa").Replace(s, "e", 1);
    yield return new Regex("aa").Replace(s, "bb", 1);
    yield return new Regex("ba").Replace(s, "abbb", 1);

    yield return new Regex("bb").Replace(s, "aa", 1);

    var en0 = generate(new Regex("e").Replace(s, "", 1)).GetEnumerator();
    var en1 = generate(new Regex("aaaa").Replace(s, "e", 1)).GetEnumerator();
    var en2 = generate(new Regex("aa").Replace(s, "bb", 1)).GetEnumerator();
    var en3 = generate(new Regex("ba").Replace(s, "abbb", 1)).GetEnumerator();
    var en4 = generate(new Regex("bb").Replace(s, "aa", 1)).GetEnumerator();

    while(true)
    {
        en0.MoveNext(); yield return en0.Current;
        en1.MoveNext(); yield return en1.Current;
        en2.MoveNext(); yield return en2.Current;
        en3.MoveNext(); yield return en3.Current;
        en4.MoveNext(); yield return en4.Current;
    }
}

this answer 中,Eric Lippert 建议不要使用枚举器。

有没有一种不用显式枚举器的好方法来表达上述内容?


上面的生成器是干什么用的?

在 Charles C. Pinter 的 A Book of Abstract Algebra 中,练习 5.F.3 指出:

G 是组 {e, a, b, b^2, b^3, ab ab^2, ab^3},其生成器满足 a^4 = ea^2 = b^2ba = ab^3。写出G的表格。 (G被称为四元组。)

以下程序:

using System;
using System.Collections.Generic;
using System.Linq;

using System.Text.RegularExpressions;

namespace pinter_5.F._1
{
    class Program
    {
        static IEnumerable<string> generate(string s)
        {
            yield return new Regex("e").Replace(s, "", 1);

            yield return new Regex("aaaa").Replace(s, "e", 1);
            yield return new Regex("aa").Replace(s, "bb", 1);
            yield return new Regex("ba").Replace(s, "abbb", 1);

            yield return new Regex("bb").Replace(s, "aa", 1);

            var en0 = generate(new Regex("e").Replace(s, "", 1)).GetEnumerator();
            var en1 = generate(new Regex("aaaa").Replace(s, "e", 1)).GetEnumerator();
            var en2 = generate(new Regex("aa").Replace(s, "bb", 1)).GetEnumerator();
            var en3 = generate(new Regex("ba").Replace(s, "abbb", 1)).GetEnumerator();
            var en4 = generate(new Regex("bb").Replace(s, "aa", 1)).GetEnumerator();

            while(true)
            {
                en0.MoveNext(); yield return en0.Current;
                en1.MoveNext(); yield return en1.Current;
                en2.MoveNext(); yield return en2.Current;
                en3.MoveNext(); yield return en3.Current;
                en4.MoveNext(); yield return en4.Current;
            }
        }

        static void Main(string[] args)
        {
            var G = new List<string>() { "e", "a", "b", "bb", "bbb", "ab", "abb", "abbb" };

            foreach (var x in G)
            {
                foreach (var y in G)
                {
                    var result = generate(x + y).First(elt => G.Contains(elt));

                    Console.Write($"{x,-5} {y,-5} = {result,-5} |");
                }
                Console.WriteLine();
            }
        }
    }
}

显示表格:

【问题讨论】:

  • 您可以使用Zip,因为您假设en* 序列的长度相同
  • 嘿@Rob。我知道您可以像这样压缩两个 IEnumerable:generate(...).Zip(generate(...), selector。但不清楚将什么指定为selector
  • 选择器为序列中的每一对创建一个新对象,因此您可能有类似a.Zip(b, (left, right) =&gt; new { a = left, b = right }) 的东西,它将[1,2,3] [4,5,6] 转换为[{a = 1, b = 4}, {a = 2, b = 5}, {a = 3, b = 6}]
  • 是的——那很好。在上面的示例中,您将遍历 zip,例如:foreach(var pair in zip) { yield return pair.a; yield return pair.b; }
  • 澄清:有时使用枚举器是不可避免的;有时您确实需要对序列的枚举方式进行细粒度控制。但我的偏好会是构建一个更高级别的序列操作——比如ZipMany——它使用枚举器,然后将更高级别的操作应用于您的问题。尽量保持机制代码和业务代码分开。

标签: c# generator yield-return ienumerator


【解决方案1】:

在上面的 cmets 中,Rob 提到了一种方法是使用 Zip

这是使用Zipgenerate 版本:

static IEnumerable<string> generate(string s)
{
    yield return new Regex("e").Replace(s, "", 1);

    yield return new Regex("aaaa").Replace(s, "e", 1);
    yield return new Regex("aa").Replace(s, "bb", 1);
    yield return new Regex("ba").Replace(s, "abbb", 1);

    yield return new Regex("bb").Replace(s, "aa", 1);

    var seq =
        generate(new Regex("e").Replace(s, "", 1))
            .Zip(generate(new Regex("aaaa").Replace(s, "e", 1)), (a, b) => new { a = a, b = b }).SelectMany(elt => new[] { elt.a, elt.b })
            .Zip(generate(new Regex("aa").Replace(s, "bb", 1)), (a, b) => new { a = a, b = b }).SelectMany(elt => new[] { elt.a, elt.b })
            .Zip(generate(new Regex("ba").Replace(s, "abbb", 1)), (a, b) => new { a = a, b = b }).SelectMany(elt => new[] { elt.a, elt.b })
            .Zip(generate(new Regex("bb").Replace(s, "aa", 1)), (a, b) => new { a = a, b = b }).SelectMany(elt => new[] { elt.a, elt.b });

    foreach (var elt in seq) yield return elt;
}

【讨论】:

  • SelectMany() 将以不正确的顺序将它们展平以用于&gt;2 序列。它将生成[a1, c1, b1, c2],而不是[a1, b1, c1, a2, b2, c2]。此外,zip 将在任一序列完成后结束。在这里,c 将首先完成(因为它的发射速度是原来的两倍)并且您将丢失来自ab 的项目。序列越多,这种效果就越差。
  • @Rob 对于这个特定的应用程序,元素的顺序无关紧要。此外,每个子序列都是无限的,所以没有一个会在另一个之前完成。
【解决方案2】:

Eric Lippert 在上面引用的帖子中包含一个ZipMany 函数。这是使用ZipManygenerate 版本:

static IEnumerable<string> generate(string s)
{
    yield return new Regex("e").Replace(s, "", 1);

    yield return new Regex("aaaa").Replace(s, "e", 1);
    yield return new Regex("aa").Replace(s, "bb", 1);
    yield return new Regex("ba").Replace(s, "abbb", 1);

    yield return new Regex("bb").Replace(s, "aa", 1);

    var seq =
        ZipMany(new[]
            {
                generate(new Regex("e").Replace(s, "", 1)),
                generate(new Regex("aaaa").Replace(s, "e", 1)),
                generate(new Regex("aa").Replace(s, "bb", 1)),
                generate(new Regex("ba").Replace(s, "abbb", 1)),
                generate(new Regex("bb").Replace(s, "aa", 1))
            },
            elts => elts).SelectMany(items => items);

    foreach (var elt in seq) yield return elt;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 2019-08-25
    • 2021-05-22
    相关资源
    最近更新 更多