【问题标题】:Filter over IEnumerable ... .Where?过滤 IEnumerable ... 。在哪里?
【发布时间】:2017-03-31 22:27:52
【问题描述】:

我做了一个函数来生成奇数:

static IEnumerable<int> OddNumbers()
{
  int n = 1;
  while (true)
    yield return 1 + 2 * (n++ - 1);
}

如何查看和过滤此列表?我正在尝试删除某个数字factor 的所有倍数,我是这样写的:

using (var oddNumbers = OddNumbers().GetEnumerator())
{
  oddNumbers.MoveNext();
  int factor = oddNumbers.Current;

  yield return factor;
  oddNumbers = oddNumbers.Where(x => x % factor != 0);
}

但我被告知

The type arguments for method `System.Linq.Enumerable.Where<TSource>(
  this System.Collections.Generic.IEnumerable<TSource>,
  System.Func<TSource,bool>)' cannot be inferred from the usage.
Try specifying the type arguments explicitly`

【问题讨论】:

  • 声明oddNumbers为:IEnumerable&lt;int&gt; oddNumbers=OddNumbers();。然后使用using 中的另一个变量作为枚举器。
  • oddNumbersIEnumerator&lt;int&gt;,但 Where 需要 IEnumerable&lt;int&gt;。您可以使用OddNumbers().Where(x =&gt; x % factor != 0)
  • 呃……所有这些操作看起来都无限的可怕……
  • oddNumbersIEnumerator,但不是 IEnumerable。我看不出你怎么能在上面执行Where
  • 如果你能告诉我们你试图解决的整个问题会很好。

标签: c# generator ienumerable


【解决方案1】:

据我了解,没有必要直接访问枚举器,可以单独使用Linq完成,如下所示。

var FilteredNumbers = OddNumbers().Where(x => x % factor != 0);

【讨论】:

  • 但是一定要对可枚举应用任何防护,例如Take(),以防止以后迭代整个集合时出现无限循环:foreach、.ToList()。等等……
  • 我反复过滤集合,并希望下一个过滤器使用前一个的输出。有没有办法就地过滤集合并存储结果?准备好再次过滤了吗?
【解决方案2】:

您可以使用 Linq

   // Initial generator
   static IEnumerable<int> OddNumbers() {
     for (int n = 1; ; n += 2) // for loop is far better than while here
       yield return n; 
   }  

   ...

   var result = OddNumbers()
     .Where(x => x % factor ! = 0);

或者修改生成器本身:

   static IEnumerable<int> OddNumbersAdvanced(int factorToExclude = int.MinValue) {
     for (int n = 1; ; n += 2)
       if (n % factorToExclude != 0) 
          yield return n;  
   }  

   ...

   var result = OddNumbersAdvanced(factor);

通过使用foreach循环:

  foreach (int item in result) {
    //TODO: put relevant code here; do not forget to break the loop
  }

【讨论】:

  • 我反复过滤集合,并希望下一个过滤器使用前一个的输出。有没有办法就地过滤集合并存储结果?准备好再次过滤了吗?
  • 您可以堆叠过滤器:var result = OddNumbers().Where(item =&gt; item % factor ! = 0).Where(item =&gt; item &lt; 100000).Where(item =&gt; (item * item) % 5 == 1); 只需放入所需数量的Where
  • @theonlygusti:OddNumbers() 生成器返回无限多的项目,所以小心.ToList() 这样的简单解决方案。将TakeWhile()Take() 放在.ToList() 之前
  • 我从不想要一个列表。我不想将其用作列表。不过谢谢。
  • @theonlygusti:我明白了;我刚刚阅读了“我如何通过和过滤此列表”。无限多的items 是可以的(有一些限制,例如你不能放OrderByToList() 等),无限多的过滤器 不是(除非它们是不是任意的过滤器)。
【解决方案3】:

由于您需要它来生成幸运数字序列,这里是我编写的一些代码,用于使用迭代器完成此操作。请注意,这样做效率很低,但希望您这样做只是为了好玩或学习

static IEnumerable<int> LuckyNumbers(IEnumerable<int> all = null, int n = 2, int step = 0) {
    if (step == 0) {
        all = Enumerable.Range(1, int.MaxValue); // start with all numbers
        yield return 1;
        step++;
    }            
    // apply a filter for current "n" (starting with 2)
   var filtered = Filtered(all, n);
   // get next item from the sequence (skip items first, because this sequence represents whole lucky number sequence, starting from 1)
    var current = filtered.Skip(step).First();
    yield return current;
    step++;
    // now recursive call back into LuckyNumber
    foreach (var other in LuckyNumbers(filtered, current, step)) {
        yield return other;
    }
}

static IEnumerable<int> Filtered(IEnumerable<int> previous, int n) {
    // filter out each n-th item
    return previous.Where((x, i) => (i + 1)%n != 0);
}

这样使用:

foreach (var next in LuckyNumbers().Take(10)) {
    Console.WriteLine(next);
}

【讨论】:

  • 您告诉我“这与 X 的可分性无关(因此,与 x % 因子无关)”但我在您自己的答案中看到:“.Where((x, i) => (i + 1)%n != 0);"
  • @theonlygusti 在我的回答中,我划分的不是 x,而是 i(序列中项目的索引,而不是数字本身)。
  • 我在我最新的解决方案中这样做。我在问题中犯了一个错误。
猜你喜欢
  • 2016-07-30
  • 1970-01-01
  • 1970-01-01
  • 2018-04-29
  • 2013-01-23
  • 2018-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多