代码之家  ›  专栏  ›  技术社区  ›  CrimsonX

如何正确地将lambda表达式的“var”结果转换为具体类型?

  •  3
  • CrimsonX  · 技术社区  · 15 年前

    所以我想了解更多关于lambda表达式的信息。我读 this question 在StackOverflow上,同意所选的答案,并尝试使用C中的控制台应用程序使用简单的LINQ表达式实现算法。

    我的问题是:如何将lambda表达式的“var结果”转换为一个可用的对象,然后我可以打印结果?

    我也希望能对我宣布 outer => outer.Value.Frequency

    (我读过很多关于lambda表达式的解释,但更多的解释会有所帮助)

    C#
    //Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}
    
    //Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}
    
    //The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.
    
    //If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.
    
          List<int> input = new List<int>();
          input.Add(5);
          input.Add(13);
          input.Add(6);
          input.Add(5);
          input.Add(13);
          input.Add(7);
          input.Add(8);
          input.Add(6);
          input.Add(5);      
    
          Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();
    
          foreach (int number in input)
          {
            if (!dictionary.ContainsKey(number))
            {
              dictionary.Add(number, new FrequencyAndValue(1, number) );
            }
            else
            {
              dictionary[number].Frequency++;
            }
          }
    
          var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);
    
          // How to translate the result into something I can print?? 
    

    有关包含打印命令的答案,请参阅 my answer here .

    4 回复  |  直到 15 年前
        1
  •  27
  •   Eric Lippert    15 年前

    如何将lambda表达式的“var result”转换为一个可用的对象,然后我可以打印结果?

    首先,“lambda表达式”只是表达式的形式部分 a=>b . 查询的其余部分只是一个以lambda为参数的方法调用。

    总之,如果我能教人们一件关于Linq的事情,那就是:“结果”不是 结果 关于这个问题, 是查询本身 .

    如果要查看结果,请查询每个结果:

    foreach(var item in result)
        Console.WriteLine(item.ToString());
    

    当我声明outer=>outer.value.frequency时,我也希望能对所发生的事情有一个深入的解释。

    当然。我们首先计算出所涉及的所有类型。我们看到lambda是一个接受keyValuePair并返回int的函数,因此我们生成一个方法

    static private int MyLambda(KeyValuePair<int, FrequencyAndValue> outer)
    {
        return outer.Value.Frequency;
    }
    

    接下来,我们采用该方法并从中创建委托:

    var result = dictionary.OrderByDescending(
        new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));
    

    重写扩展方法调用:

    var result = Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
        dictionary,
        new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));
    

    重写var:

    IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> result =
        Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
        dictionary,
        new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));
    

    我希望您同意,您输入的代码比这一团糟的代码可读性要好得多。类型推断岩。

    结果是一个对象,它表示按给定键对字典排序的能力。仔细阅读:它代表 能力 按那个键对字典排序。直到你要求一个结果,它才真正做到这一点;到目前为止,它只是一个对象,上面写着“当要求一个结果时,按这个键对字典进行排序”。

    假设你要求一个结果。它如何计算排序列表?它向字典询问每个元素。然后它对每个元素调用mylambda,这会返回一个整数,所以我们现在有了一对字典键值对和整数。然后,它构建一个按该整数排序的对列表。然后它一次一个地分发列表中的元素,正如您所要求的那样。

    我们看到lambda是一个函数,它接受一个keyValuePair并返回一个int”-您是如何确定的?我没有从方法返回值中看到它,也没有在orderByDescending()中记录它。

    啊,我看到了困惑;出于教学原因,我在语义分析的确切顺序上撒了一点谎。

    我们如何进行这种类型推理是C中更微妙和有趣的部分之一。

    这就是它的工作原理。

    我们看到orderByDescending声明为:

    static IOrderedEnumerable<T> OrderByDescending<T, K>(
        this IEnumerable<T> sequence, 
        Func<T, K> keyExtractor)
    

    我们看到有一个对这个方法的潜在调用:

    OrderByDescending(dictionary, o=>o.Value.Frequency)
    

    但我们不知道T和K是什么。所以我们从所有不是lambda的东西开始。你的字典实现了 IEnumerable<KeyValuePair<int, FrequencyOrValue>> 所以我们先说“T可能是 KeyValuePair<int, FrequencyOrValue> “。

    在这一点上,我们无法从那些不是lambda的东西中推断出其他的东西,所以我们开始研究lambda。我们看到我们有一个lambda o=>o.Value.Frequency 到目前为止,我们已经确定钥匙拔出器的类型是 Func<KeyValuePair<int, FrequencyOrValue>, K> 我们仍然在寻找k,所以我们假设lambda实际上是:

    (KeyValuePair<int, FrequencyOrValue> o)=>{return o.Value.Frequency;}
    

    我们问 它是否结合 ?对!是的。我们可以成功地编译这个lambda而不出错,当我们这样做时,我们看到它的所有返回语句都返回一个int。

    因此,我们推断k是int,现在我们对整个事物进行了一个完整的类型分析。

    这是一个相当直截了当的推论;他们可能会变得更加奇怪。如果您对这个主题特别感兴趣,请参阅我博客上的“类型推断”档案。

    http://blogs.msdn.com/ericlippert/archive/tags/Type+Inference/default.aspx

    特别是,这里有一段我解释上述内容的视频,还有一些其他有趣的案例:

    http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx

        2
  •  2
  •   Jhonny D. Cano -Leftware-    15 年前

    orderByDescending函数将返回IEnumerable,实际上是IOrderenumerable,其中tsource是原始可枚举的类型源。

    使用字典时,orderByDescending将返回:

     IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>>
    

    对象,将根据提供的表达式进行排序。

        3
  •  1
  •   Anthony Pegram    15 年前
    var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);
    

    这条线给你一个 IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> 调用的结果。至于lambda,它是

    Func<KeyValuePair<int,FrequencyAndValue>, int>
    

    这意味着它接受 KeyValuePair<int, FrequencyAndValue> 参数(您正在调用outer),并返回与对值的frequency属性相对应的整数。所以产生的IORDerenumerable是按照频率以相反的顺序排序的。

        4
  •  0
  •   CrimsonX    15 年前

    为了完整的回答文档,我使用更通用的“项”以及更具体的“可撤销”来打印输出。

    C#
    static void Main(string[] args)
        {
    
          //Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}
    
          //Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}
    
          //The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.
    
          //If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.
    
          List<int> input = new List<int>();
          input.Add(5);
          input.Add(13);
          input.Add(6);
          input.Add(5);
          input.Add(13);
          input.Add(7);
          input.Add(8);
          input.Add(6);
          input.Add(5);      
    
          Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();
    
          foreach (int number in input)
          {
            if (!dictionary.ContainsKey(number))
            {
              dictionary.Add(number, new FrequencyAndValue(1, number) );
            }
            else
            {
              dictionary[number].Frequency++;
            }
          }
    
          var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);
    
          // BEGIN Priting results with the help of stackoverflow answers 
          Console.Write("With Items: ");    
          foreach (var item in result)
          {
            for (int i = 0; i < item.Value.Frequency; i++)
            {
              Console.Write(item.Value.Value + " ");
            }
            //Console.WriteLine(item.Value.Frequency + " " + item.Value.Value);
          }
          Console.WriteLine();
    
          Console.Write("With IOrderedEnumerable: ");    
          IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> myres = result;
          foreach (KeyValuePair<int, FrequencyAndValue> fv in myres)
          {
            for(int i = 0; i < fv.Value.Frequency; i++ )
            {
              Console.Write(fv.Value.Value + " ");
            }
          }
          Console.WriteLine();
          // END Priting results with the help of stackoverflow answers 
          Console.ReadLine();
        }
        class FrequencyAndValue
        {
          public int Frequency{ get; set;}
          public int Value{ get; set;}
          public FrequencyAndValue(int myFreq, int myValue)
          {
            Value = myValue;
            Frequency = myFreq;
          }
        }
    }