【问题标题】:How to properly translate the "var" result of a lambda expression to a concrete type?如何正确地将 lambda 表达式的“var”结果转换为具体类型?
【发布时间】:2010-03-26 14:16:39
【问题描述】:

所以我正在尝试更多地了解 lambda 表达式。我在 stackoverflow 上阅读了this question,同意所选答案,并尝试使用 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

【问题讨论】:

  • 通过观看 Jon Skeet 的演讲,我了解了很多关于 LINQ 的工作原理,请参阅此页面了解更多信息 csharpindepth.com/Talks.aspx。我认为这是第 6 节,但它们都非常有用。
  • @Matt - 谢谢,我去看看!

标签: c# lambda type-inference


【解决方案1】:

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

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

无论如何,如果我可以教人们关于 LINQ 的一件事,那就是:“结果”不是查询的 结果,它是查询本身 .

如果您想查看结果,请针对每个结果询问查询:

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

如果我声明外部 => external.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&lt;KeyValuePair&lt;int, FrequencyOrValue&gt;&gt;,所以我们首先说“T 可能是KeyValuePair&lt;int, FrequencyOrValue&gt;”。

在这一点上,我们无法从不是 lambdas 的东西中推断出其他任何东西,所以我们开始研究 lambdas。我们看到我们有一个 lambda o=&gt;o.Value.Frequency,到目前为止,我们已经确定 keyExtractor 的类型是 Func&lt;KeyValuePair&lt;int, FrequencyOrValue&gt;, K&gt;,我们仍在寻找 K。所以我们假设 lambda 实际上是:

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

我们问 它是否绑定?是的!是的,它确实。我们可以成功地编译这个 lambda 而不会出错,当我们这样做时,我们看到它的所有 return 语句都返回一个 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

【讨论】:

  • +1、+1、+1。 LINQ 创建查询。有些函数会迭代这些查询,但在您使用它们迭代查询之前,它只是一个查询
  • @Randolpho - 你怎么投票 3 次?我试过了,不行!,骗子。
  • 嘿。有很多事情要做。
  • “或者你想要更多的深度?”稍微反汇编的 CIL 会很好。
  • @Crimson,字典包含一个 KeyValuePair 集合。在您的示例中,TKey 是 int 类型,TValue 是 FrequencyAndValue 类型。因此,如果您的字典要使用 OrderByDescending 扩展方法,则输入将是字典包含的元素类型 (KeyValuePair)。您将 lambda 的返回值指定为 outer.Value.Frequency,它是一个整数。这就是为什么“lambda 是一个接受 KeyValuePair 并返回一个 int 的函数”的原因。 ...编辑:是的,在您的最后评论中。
【解决方案2】:

OrderByDescending 函数将返回一个 IEnumerable,实际上是一个 IOrderedEnumerable,其中 TSource 是原始枚举的类型源。

当您使用字典时,OrderByDescending 将返回:

 IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>>

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

【讨论】:

    【解决方案3】:
    var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);
    

    这一行给你一个IOrderedEnumerable&lt;KeyValuePair&lt;int, FrequencyAndValue&gt;&gt; 调用的结果。至于 lambda,它的类型是

    Func<KeyValuePair<int,FrequencyAndValue>, int>
    

    这意味着它接受KeyValuePair&lt;int, FrequencyAndValue&gt; 参数(您将其称为外部),并返回与该对值的频率属性相对应的整数。因此,生成的 IOrderedEnumerable 将按频率以相反的顺序排序。

    【讨论】:

      【解决方案4】:

      为了获得完整的答案文档,我使用更通用的“项目”以及更具体的 IOrderedEnumerable 打印输出。

      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;
            }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-03-31
        • 1970-01-01
        • 2022-12-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多