如何在C#中用函数语句替换for循环?

如何在C#中用函数语句替换for循环?,c#,linq,functional-programming,lambda,for-loop,C#,Linq,Functional Programming,Lambda,For Loop,一位同事曾经说过,每次我写一个for循环,上帝都在杀死一只小猫 当被问及如何避免for循环时,他的答案是使用函数式语言。但是,如果您使用的是非函数式语言,比如C#,那么有哪些技术可以避免循环或通过重构来消除循环?也许是lambda表达式和LINQ?如果是,怎么做 问题 因此,问题归结为: 为什么循环是坏的?或者,在什么情况下循环应该避免,为什么 您能否提供C#代码示例,说明它在使用循环之前和使用循环之后的外观 你的同事不对。因为循环本身并不坏。它们干净易读,不容易出错。因为循环不会杀人(或小猫、

一位同事曾经说过,每次我写一个for循环,上帝都在杀死一只小猫

当被问及如何避免for循环时,他的答案是使用函数式语言。但是,如果您使用的是非函数式语言,比如C#,那么有哪些技术可以避免循环或通过重构来消除循环?也许是lambda表达式和LINQ?如果是,怎么做

问题

因此,问题归结为:

  • 为什么循环是坏的?或者,在什么情况下循环应该避免,为什么
  • 您能否提供C#代码示例,说明它在使用循环之前和使用循环之后的外观

  • 你的同事不对。因为循环本身并不坏。它们干净易读,不容易出错。

    因为循环不会杀人(或小猫、小狗或三脚猫)。人们杀人。
    因为循环本身并不坏。然而,与其他任何事情一样,使用它们的方式也可能是不好的。

    函数构造通常比循环更清楚地表达您的意图,在这种情况下,您操作某些数据集并希望转换、过滤或聚合元素

    当您想要重复执行某些操作时,循环非常合适


    比如说

    int x = array.Sum();
    
    更清楚地表达你的意图,而不是

    int x = 0;
    for (int i = 0; i < array.Length; i++)
    {
        x += array[i];
    }
    
    intx=0;
    for(int i=0;i
    如果存在更有效的替代方案,则for循环有时是不好的。例如搜索,对列表排序然后使用快速排序或二进制排序可能更有效。或者当您迭代数据库中的项时。在数据库中使用基于集合的操作通常比在项目上迭代要有效得多

    否则,如果for循环,特别是for-each循环最有意义且可读,那么我将使用它,而不是将其转换为不那么直观的内容。我个人不相信这些听起来像宗教的说法“总是这样做,因为那是唯一的方法”。相反,最好有指导方针,并了解在什么情况下应用这些指导方针是合适的。这是好的,你问为什么的

    一个简单(实际上毫无意义)的例子:

    循环

    // mystrings is a string array
    List<string> myList = new List<string>();
    foreach(string s in mystrings)
    {
        if(s.Length > 5)
        {
            myList.add(s);
        }
    }
    
    //mystrings是一个字符串数组
    List myList=新列表();
    foreach(mystrings中的字符串s)
    {
    如果(s.长度>5)
    {
    myList.add(s);
    }
    }
    
    Linq

    // mystrings is a string array
    List<string> myList = mystrings.Where<string>(t => t.Length > 5).ToList<string>();
    
    //mystrings是一个字符串数组
    List myList=mystrings.Where(t=>t.Length>5.ToList();
    
    在我的书中,第二个看起来更加整洁和简单,尽管第一个没有什么问题

  • 因为循环并不坏。保持for循环有许多非常合理的理由
  • 您通常可以通过使用C#中的LINQ对for循环进行返工来“避免”它,这提供了一种更具声明性的语法。这可能是好的,也可能是坏的,取决于具体情况:
  • 比较以下各项:

    var collection = GetMyCollection();
    for(int i=0;i<collection.Count;++i)
    {
         if(collection[i].MyValue == someValue)
              return collection[i];
    }
    
    与林克相比:

    var collection = GetMyCollection();
    return collection.FirstOrDefault(item => item.MyValue == someValue);
    
    就我个人而言,这三个选项都有自己的位置,我都使用它们。这是一个为您的场景使用最合适的选项的问题。

    for loop是,比方说,“坏的”,因为它意味着CPU中的分支预测,当分支预测失败时,性能可能会下降


    但是CPU(具有97%的分支预测准确率)和具有循环展开等技术的编译器使循环性能降低可以忽略不计。

    您可以充分重构代码,这样您就不会经常看到它们。一个好的函数名肯定比for循环更具可读性

    以AndyC为例:

    环路


    这是一个过于简单的例子,但你明白我的意思。

    任何语言中的任何构造都是有原因的。它是用来完成任务的工具。意味着达到目的。在任何情况下,都有适当地使用它的方式,也就是说,在语言的精神和方式范围内,以一种清晰简洁的方式滥用它。这适用于极不协调的
    goto
    语句以及
    for
    循环难题,以及
    while
    do while
    switch/case
    if-then-else
    等。如果
    for
    循环是您所做工作的正确工具,使用它,您的同事将需要接受您的设计决策。

    您的同事错误地认为for循环在所有情况下都不好,但正确的是,它们可以在功能上重写

    假设您有一个如下所示的扩展方法:

    void ForEach<T>(this IEnumerable<T> collection, Action <T> action)
    {
        foreach(T item in collection)
        {
            action(item)
        }
    }
    
    mycollection.ForEach(x => x.DoStuff());
    
    现在这可能不是很有用。但是,如果随后将ForEach扩展方法的实现替换为使用多线程方法,则可以获得并行性的优势

    显然,这并不总是可行的,这种实现只有在循环迭代彼此完全独立的情况下才有效,但它是有用的


    另外:对于那些说某些编程结构总是错误的人,一定要小心。

    这取决于循环中的内容,但他/她可能指的是递归函数

        //this is the recursive function
        public static void getDirsFiles(DirectoryInfo d)
        {
            //create an array of files using FileInfo object
            FileInfo [] files;
            //get all files for the current directory
            files = d.GetFiles("*.*");
    
            //iterate through the directory and print the files
            foreach (FileInfo file in files)
            {
                //get details of each file using file object
                String fileName = file.FullName;
                String fileSize = file.Length.ToString();
                String fileExtension =file.Extension;
                String fileCreated = file.LastWriteTime.ToString();
    
                io.WriteLine(fileName + " " + fileSize + 
                   " " + fileExtension + " " + fileCreated);
            }
    
            //get sub-folders for the current directory
            DirectoryInfo [] dirs = d.GetDirectories("*.*");
    
            //This is the code that calls 
            //the getDirsFiles (calls itself recursively)
            //This is also the stopping point 
            //(End Condition) for this recursion function 
            //as it loops through until 
            //reaches the child folder and then stops.
            foreach (DirectoryInfo dir in dirs)
            {
                io.WriteLine("--------->> {0} ", dir.Name);
                getDirsFiles(dir);
            }
    
        }
    

    for循环没有什么错,但以下是人们可能更喜欢函数式/声明式方法(如LINQ)的一些原因,在LINQ中,您声明了自己想要的东西,而不是如何获得它:-

  • 函数方法可能更容易通过PLINQ手动或编译器进行并行化。随着CPU迁移到更多的内核,这可能变得更加重要

  • 函数方法使得在多步骤流程中更容易实现延迟计算,因为您可以将中间结果作为尚未完全计算的简单变量传递到下一步,而不是完全计算第一步,然后将集合传递到下一步(或者不使用单独的方法和收益率声明来实现相同的程序)

  • 功能性方法
    var filteredList = myList.GetStringLongerThan(5);
    
    void ForEach<T>(this IEnumerable<T> collection, Action <T> action)
    {
        foreach(T item in collection)
        {
            action(item)
        }
    }
    
    mycollection.ForEach(x => x.DoStuff());
    
        //this is the recursive function
        public static void getDirsFiles(DirectoryInfo d)
        {
            //create an array of files using FileInfo object
            FileInfo [] files;
            //get all files for the current directory
            files = d.GetFiles("*.*");
    
            //iterate through the directory and print the files
            foreach (FileInfo file in files)
            {
                //get details of each file using file object
                String fileName = file.FullName;
                String fileSize = file.Length.ToString();
                String fileExtension =file.Extension;
                String fileCreated = file.LastWriteTime.ToString();
    
                io.WriteLine(fileName + " " + fileSize + 
                   " " + fileExtension + " " + fileCreated);
            }
    
            //get sub-folders for the current directory
            DirectoryInfo [] dirs = d.GetDirectories("*.*");
    
            //This is the code that calls 
            //the getDirsFiles (calls itself recursively)
            //This is also the stopping point 
            //(End Condition) for this recursion function 
            //as it loops through until 
            //reaches the child folder and then stops.
            foreach (DirectoryInfo dir in dirs)
            {
                io.WriteLine("--------->> {0} ", dir.Name);
                getDirsFiles(dir);
            }
    
        }
    
    void For<T>(T initial, Func<T,bool> whilePredicate, Func<T,T> step, Action<T> action)
    {
        for (T t = initial; whilePredicate(t); step(t))
        {
            action(t);
        }
    }
    
    let x = someList;
    y = []
    for x' in x
        y.Add(f x')
    
    z = []
    for y' in y
        z.Add(g y')
    
    let apply2 a g b f x = a g (b f x)
    
    let mapmap g f = apply2 map g map f
    let mapfilter g f = apply2 map g filter f
    
    for (int index = 0; index < args.Length; ++index)
        Console.WriteLine(args[index]);
    
    WriteValuesToTheConsole(args, 0);
    
    
    static void WriteValuesToTheConsole<T>(T[] values, int startingIndex)
    {
        if (startingIndex < values.Length)
        {
            Console.WriteLine(values[startingIndex]);
            WriteValuesToTheConsole<T>(values, startingIndex + 1);
        }
    }