可以';我不明白这个IEnumerable为什么会给出这个答案

本文关键字:为什么 答案 IEnumerable 明白 可以 | 更新日期: 2023-09-27 18:29:26

以下代码的答案是5

有人能解释一下为什么会这样吗?如果将int d1 = x.Current替换为d1 = x.Current,并在while循环上方声明d1,答案将是2,我理解为什么是这样,但我不知道为什么是5。

IEnumerable<int> num = new []{10,11,12,13,14,15,16};
IEnumerable<int> div = new [] {2,3,5};
var lazy = Enumerable.Empty<int>();
var x = div.GetEnumerator();
while(x.MoveNext()){
    int d1 = x.Current;
    lazy = lazy.Concat(num.Where(s=>s % d1 == 0));
}
int count = lazy.Distinct().Count();
Console.WriteLine("{0}",count);

编辑:这是给你答案2的片段。

IEnumerable<int> num = new []{10,11,12,13,14,15,16};
IEnumerable<int> div = new [] {2,3,5};
var lazy = Enumerable.Empty<int>();
var x = div.GetEnumerator();
int d1;
while(x.MoveNext()){
    d1 = x.Current;
    lazy = lazy.Concat(num.Where(s=>s % d1 == 0));
}
int count = lazy.Distinct().Count();
Console.WriteLine("{0}",count);

可以';我不明白这个IEnumerable为什么会给出这个答案

(Peter把我打得落花流水,但当他的答案出现时,我已经完成了一半,所以我无论如何都会发布它。)

您可以通过检测代码来深入了解差异。lambda表达式中的仪器提供了关键的见解:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Baseline.");
        Test1();
        Console.WriteLine("Modified.");
        Test2();
    }
    static void Test1()
    {
        IEnumerable<int> num = new[] { 10, 11, 12, 13, 14, 15, 16 };
        IEnumerable<int> div = new[] { 2, 3, 5 };
        var lazy = Enumerable.Empty<int>();
        var x = div.GetEnumerator();
        while (x.MoveNext())
        {
            int d1 = x.Current;
            Console.WriteLine("d1 = " + d1);
            lazy = lazy.Concat(num.Where(s => {bool result = s % d1 == 0; Console.WriteLine("s = " + s + ", d1 = " + d1); return result;}));
            Console.WriteLine("lazy has " + lazy.Count());
        }
        Console.WriteLine("Evaluating lazy.Distinct().Count()");        
        int count = lazy.Distinct().Count();
        Console.WriteLine("{0}", count);
    }
    static void Test2()
    {
        IEnumerable<int> num = new[] { 10, 11, 12, 13, 14, 15, 16 };
        IEnumerable<int> div = new[] { 2, 3, 5 };
        var lazy = Enumerable.Empty<int>();
        var x = div.GetEnumerator();
        int d1;
        while (x.MoveNext())
        {
            d1 = x.Current;
            Console.WriteLine("d1 = " + d1); 
            lazy = lazy.Concat(num.Where(s => {bool result = s % d1 == 0; Console.WriteLine("s = " + s + ", d1 = " + d1); return result;}));
            Console.WriteLine("lazy has " + lazy.Count());
        }
        Console.WriteLine("Evaluating lazy.Distinct().Count()");
        int count = lazy.Distinct().Count();
        Console.WriteLine("{0}", count);
    }
}

打印"Evaluating.Distinct().Count()"之后,您会注意到两件可能会让您感到惊讶的事情。

首先,该求值需要重新运行在循环中声明的lambda表达式。将"懒惰"视为整数的集合很容易,但这是错误的。事实上,它是一个用于创建整数集合的函数,因此计算不同元素需要重新运行该函数。

其次,您会注意到d1的值在两个评估之间是不同的。在第一种情况下,d1是2,在第二种情况下d1是5。正如Peter Duniho所指出的,原因是在循环外声明d1允许它保留循环结束时的值(因此有5,div序列中的最后一个值),在循环内声明它需要重新计算它(因此有2,div序列的第一个元素)。

这是因为延迟执行lazy.Concat(num.Where(s=>s % d1 == 0))中的lambda表达式。

当您在循环中声明变量时,匿名方法的每个实例都会获得自己的变量。但是,当您在循环外声明变量时,它们都共享同一个变量,当然,当lambda表达式最终在这里执行时,该变量的值只有一个值(循环中分配的最终值):

int count = lazy.Distinct().Count();

这个故事的寓意是:小心使用捕捉到的变量。它们通常很难推理,所以应该小心使用。