【问题标题】:Does C# have anything comparable to Python's list comprehensions?C# 有什么可以与 Python 的列表推导相媲美的吗?
【发布时间】:2010-09-24 07:04:31
【问题描述】:

我想在 C# 中生成一个列表。我缺少 python 的列表理解。有没有像 Python 中的列表推导或生成器表达式那样动态创建集合的 C# 方法?

【问题讨论】:

    标签: c# python list list-comprehension


    【解决方案1】:

    如果您使用的是 C# 3.0 (VS2008),那么 LINQ to Objects 可以做非常相似的事情:

    List<Foo> fooList = new List<Foo>();
    IEnumerable<Foo> extract = from foo in fooList where foo.Bar > 10 select Foo.Name.ToUpper();
    

    【讨论】:

      【解决方案2】:

      Matt 提到了查询表达式。顺便说一下,这些通常可用于 LINQ - 而不仅仅是 LINQ to Objects。 (例如,应用于 LINQ to SQL 数据上下文的相同查询将在数据库上执行过滤器和投影。)

      C# 3 中的查询表达式只是编写普通 C# 代码的语法糖——尽管查询表达式通常最终调用extension methods。 (它们不必这样做,编译器也不关心,但它们通常会这样做。)您可以对集合执行各种操作,这些操作在 C# 查询表达式中不可用,但受方法调用支持,因此值得了解这两种语法。例如,Matt 的查询表达式为:

      List<Foo> fooList = new List<Foo>();
      IEnumerable<string> extract = from foo in fooList where foo.Bar > 10 select foo.Name.ToUpper();
      

      被“预处理”成:

      List<Foo> fooList = new List<Foo>();
      IEnumerable<string> extract = fooList.Where(foo => foo.Bar > 10)
                                           .Select(foo => foo.Name.ToUpper());
      

      如果您想(比如说)根据原始集合中值的索引进行过滤,您可以使用appropriate overload of Where,它无法通过查询表达式获得:

      List<Foo> fooList = new List<Foo>();
      IEnumerable<string> extract = fooList.Where((foo, index) => foo.Bar > 10 + index)
                                           .Select(foo => foo.Name.ToUpper());
      

      或者您可以找到符合条件的最长名称的长度:

      List<Foo> fooList = new List<Foo>();
      int longestName = fooList.Where((foo, index) => foo.Bar > 10 + index)
                               .Select(foo => foo.Name)
                               .Max();
      

      (您没有在单独的方法中进行投影和最大值 - 有一个 Max 重载也需要一个投影。)

      我的观点是,使用扩展方法可以非常轻松地构建复杂的查询。

      您也提到了 Python 生成器 - C# 以 iterator blocks 的形式提供了它。事实上,这些在实现类似 LINQ 的运算符时非常有用。 (因为大多数 LINQ to Objects 都基于扩展方法,您可以将自己的运算符添加到 LINQ 中,这些运算符看起来“本机” - 尽管您不能自己更改查询表达式语法。)

      【讨论】:

      • Python 的列表解析除了从另一个列表中提取 之外,还可以从头开始创建一个列表。据我了解,您从现有列表中显示的摘录。但是 OP 在他们的帖子中提到了 generatecreate 。我认为我的回答是创造的东西。如果我错了,请纠正我。毕竟,您只看了问题...和问题answered itself ;-)
      • @Sнаđошƒаӽ:说实话,这个问题还不够清楚 - 如果这些天提出来,它就会被关闭。我不会再努力回答一个不清楚的问题......虽然我个人会使用 Enumerable.Range 作为“来源”,而不是你的答案中的 for 循环。
      • 感谢Enumerable.Range 指针。除此之外,该 C# 代码是否等同于 python 的列表理解?或者,在 C# 中有没有更优雅的方式?与 python 的单行代码相比,C# 代码看起来很庞大。
      • @Sнаđошƒаӽ:嗯,应该是string.Join(", ", Enumerable.Range(1, 50).Select(x =&gt; $"cb{x:00}"))),真的没那么长……
      • @Sнаđошƒаӽ:嗯,您只需将$"cb{x:00}" 更改为string.Format("cb{0:00}", x),所以并没有非常 太大的区别 - 但是是的,它仍然是一个改进。
      【解决方案3】:

      List&lt;T&gt;.ConvertAll 的行为就像列表推导式一样,对现有列表中的每个项目执行相同的操作,然后返回一个新集合。这是使用 Linq 的替代方法,尤其是在您仍在使用 .NET 2.0 时。

      在 Python 中,一个简单的列表理解示例:

      >>> foo = [1, 2, 3]
      >>> bar = [x * 2 for x in foo]
      >>> bar
      [2, 4, 6]
      

      对于 C# 3.0,您可以传递一个 lambda 函数来指定所需的映射函数类型。

      public static void Main()
      {
          var foo = new List<int>{ 1, 2, 3};
          var bar = foo.ConvertAll(x => x * 2);    // list comprehension
      
          foreach (var x in bar)
          {
              Console.WriteLine(x);  // should print 2 4 6
          }
      }
      

      对于 C# 2.0,您可以使用带有 Converter 委托的匿名方法来执行等效操作。

      public static void Main()
      {
          List<int> foo = new List<int>(new int[]{ 1, 2, 3});
          List<int> bar = foo.ConvertAll(new Converter<int, int>(delegate(int x){ return x * 2; }));  // list comprehension
      
          foreach (int x in bar)
          {
              Console.WriteLine(x);  // should print 2 4 6
          }
      }
      

      (注意:使用Array.ConvertAll的数组也可以这样做

      【讨论】:

        【解决方案4】:

        我知道这是一个非常晚的答案,但我也想知道 C# 是否有任何相当于 python 的列表理解的东西。据我所知,Matt CampbellJon Skeet 的答案显示了如何提取现有列表中的列表。但是 python 的列表推导也可以从头开始创建列表。所以这就是我想出的。

        首先我将展示 Python 列表推导式,然后展示我想出的 C# 等价物。

        玩具任务是创建一个这样的字符串

        cb01, cb02, cb02, ... , cb50
        

        Python 列表理解:

        s = ', '.join('cb{0:02}'.format(i+1) for i in range(50))
        

        我想出的 C# 等效项:

        string s = String.Join(", ", new Func<List<string>>( () =>
        {
            List<string> list = new List<string>();
            foreach (int i in Enumerable.Range(1, 50))
                list.Add(String.Format("cb{0:00}", i));
        
            return list;
        }).Invoke());
        

        我不确定我是否做过头。


        编辑:(来自 Jon Skeet 在他的评论中提到的,真正的单行相当于 python 的列表理解)

        String.Join(", ", Enumerable.Range(1, 50).Select(x => $"cb{x:00}")))
        

        注意$C# 6 feature。如果你还没有使用 C# 6,你可以使用旧的 String.Format() 方式。

        【讨论】:

          【解决方案5】:

          有这个:

          new List<FooBar> { new Foo(), new Bar() }
          

          它只比它的 python 等价物长一点:

          [Foo(), Bar()]
          

          然后是这样的:

          public IEnumerable<FooBar> myFooBarGenerator() {
               yield return new Foo();
               yield return new Bar();
          }
          

          这是python的等价物:

          def myFooBarGenerator():
              yield Foo()
              yield Bar()
          

          【讨论】:

            猜你喜欢
            • 2022-11-15
            • 1970-01-01
            • 1970-01-01
            • 2015-11-01
            • 1970-01-01
            • 2016-04-19
            • 2020-01-08
            • 2017-11-12
            • 1970-01-01
            相关资源
            最近更新 更多