【问题标题】:C# - TakeWhile and SkipWhile not returning?C# - TakeWhile 和 SkipWhile 不返回?
【发布时间】:2016-02-26 13:37:05
【问题描述】:

我有一个类 RekenReeks,它返回从 2 开始的数字,乘以 2。所以 {2,4,8,16,32,64}

现在我了解了 TakeWhile 和 SkipWhile 方法以及 LINQ。

所以我创建了 3 个变量,它们应该存储完全相同,但我的 Console.WriteLine 只打印 selection1 而不是 2 和 3。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            RekenReeks reeks = new RekenReeks(2, n => n * 2);
            var selection = from i in reeks
                        where i > 10
                        && i < 1000
                        select i;

        var selection2 = reeks.TakeWhile(n => n < 1000 && n > 10);

        var selection3 = reeks.SkipWhile(n => n > 1000 && n < 10);

        foreach (int i in selection)
        {
            Console.WriteLine("selection1 {0}",i);
        }

        foreach (int i in selection2)
        {
            Console.WriteLine("selection2 {0}", i);
        }

        foreach (int i in selection3)
        {
            Console.WriteLine("selection3 {0}", i);
        }

        Console.ReadLine();
    }
}


public class RekenReeks : IEnumerable<int>
{
    Func<int, int> getNew;
    int number;
    public RekenReeks(int starton, Func<int, int> getNewThing)
    {
        getNew = getNewThing;
        number = starton;
    }

    public IEnumerator<int> GetEnumerator()
    {
        yield return number;
        for (; ; )
        {

            yield return getNew(number);
            number = getNew(number);

        }

    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }


}
}

【问题讨论】:

  • 您正在多次枚举您的IEnumerable
  • 所以应该通过创建IEnumerable的3个实例来解决?
  • 可以的。
  • @DanielA.White 你在说什么?多重枚举没问题,你没有其他选择,无穷无尽的序列。

标签: c# linq lambda expression


【解决方案1】:

您的序列是无限的(理论上)。你假设太多了。您的程序的功能不可能知道您的序列是严格单调递增的。

var selection = from i in reeks
                    where i > 10
                    && i < 1000
                    select i;

这永远不会停止。因为where总是拉下一个值,它不知道它的条件会被满足,它总是去检查下一个值。

    var selection2 = reeks.TakeWhile(n => n < 1000 && n > 10);

这将采用介于 11 和 999 之间的值。由于您的序列从 2 开始,它将在第一个值处停止。

    var selection3 = reeks.SkipWhile(n => n > 1000 && n < 10);

这将跳过介于 11 和 999 之间的值。由于第一个是 2,它将不跳过任何值,因此产生 all

【讨论】:

  • 如何阻止我的 LINQ 表达式运行?
  • @Ivaro18 你为什么要这样做?你对阻止它有什么期望?如果您告诉我们您想要做什么以及您期望发生什么,我们可能会建议其他选择。
【解决方案2】:

TakeWhile()SkipWhile() 都从序列的最开头开始,当条件不满足时停止获取/跳过。

  // I've redesigned your ReekenReeks
  // [2, 4, 8, 16, 32, 64, ..., 2**30]
  var reeks = Enumerable
    .Range(1, 30)
    .Select(index => 1 << index);

例如,如果你把

  var selection2 = reeks
    .TakeWhile(n => n < 1000 && n > 10);

由于reeks 的第一项是2,条件n &lt; 1000 &amp;&amp; n &gt; 10 不满足TakeWhile 停止并返回一个 序列。正确的实现是

  var selection2 = reeks
    .SkipWhile(n => n <= 10)
    .TakeWhile(n => n < 1000);

如果你想从序列(selection3)的中间截断值,你必须使用Where

  var selection3 = reeks
    .Where(n => !(n > 1000 && n < 10)); // cut theses items off

打印出 infinine 序列时要小心,.Take() 在这里是个不错的选择:

   var selection3 = reeks
    .Where(n => n > 1000 && n < 10)
    .Take(100); // First 100 items in case the sequence is too long

【讨论】:

  • @Ivaro18:在您的代码中 SkipWhile(n =&gt; n &gt; 1000 &amp;&amp; n &lt; 10) 跳过 nothing (条件不符合第一项 - 2)然后 TakeWhile(n =&gt; n &lt; 1000 &amp;&amp; n &gt; 10) 采用 什么也没有,因为第一项是2 并且条件不满足
  • var selection2 = reeks.SkipWhile(n =&gt; n &lt;= 10).TakeWhile(n =&gt; n &lt; 1000); 返回一个空序列。我明白我做错了什么
  • 我已经运行了代码(使用我的reeks)并且我有[16, 32, 64, 128, 256, 512] 用于selection2Console.Write("[" + String.Join(", ", selection2) + "]");
  • 用你的臭味它确实有效,但为什么它不能用我的呢?
  • 哦,对不起,我的RekenReeks.getEnumerator() 中还有yield break。删除后,它也适用于我的班级
【解决方案3】:
var selection = from i in reeks
  where i > 10
  && i < 1000
  select i;

没有理由让这一切结束。当i 为1024 时,它不会产生,但它仍会检查2048 是否小于1000,或4096 小于1000,或8192 是否小于@987654328 @ 以此类推(最终要么发生溢出异常,要么n 环绕到0,然后一直设置为0 * 2,仍然是0)。

reeks.TakeWhile(n => n < 1000 && n > 10)

第一个尝试的值是2。这不满足谓词n &lt; 1000 &amp;&amp; n &gt; 10,因为2 &gt; 10 不是真的。因此,采取停止。

reeks.SkipWhile(n => n > 1000 && n < 10)

n 没有对应的 n &gt; 1000 &amp;&amp; n &lt; 10 值。因此,这与根本没有SkipWhile 相同。

看来你想要的是:

reeks.SkipWhile(n => n >= 1000 || n <= 10).TakeWhile(n => n < 1000 && n > 10)

它会跳过,直到找到第一个符合条件的数字,然后取所有符合条件的数字,直到第一个不符合条件的数字。

【讨论】:

  • var selection2 = reeks.SkipWhile(n =&gt; n &gt;= 1000 || n &lt;= 10).TakeWhile(n =&gt; n &lt; 1000 &amp;&amp; n &gt; 10); 由于某种原因使用此行仍然返回一个空序列
  • 通过删除yield break解决,另一个用户提交了作为答案,我没有删除它
  • 啊。我在那里很困惑。我得到了序列 16、32、64、128、256、512。(你可以用reeks.SkipWhile(n =&gt; n &lt;= 10).TakeWhile(n =&gt; n &lt; 1000) 稍微快一点地得到相同的序列,但这是基于序列即将到来的知识的答案。我用多余的部分回答因为它符合.SkipWhile(predicateMatchingAllUnwanted).TakeWhile(predcateMatchingWanted)的基本模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2013-04-27
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
相关资源
最近更新 更多