Ler*_*rve 32 c# linq lambda for-loop functional-programming
一位同事曾经说过,每当我写一个for-loop时,上帝就会杀死一只小猫.
当被问及如何避免for循环时,他的回答是使用函数式语言.但是,如果你坚持使用非功能性语言,比如说C#,有什么技术可以避免for循环或通过重构来摆脱它们?有了lambda表达式和LINQ吗?如果是这样,怎么样?
问题
所以问题归结为:
dtb*_*dtb 30
在您操作某些数据集并希望转换,过滤或聚合元素的情况下,功能构造通常比for循环更清楚地表达您的意图.
当您想要重复执行某些操作时,循环非常合适.
例如
int x = array.Sum();
更明确地表达了你的意图
int x = 0;
for (int i = 0; i < array.Length; i++)
{
    x += array[i];
}
Jul*_*iet 16
为什么for循环不好?或者,在什么情况下for循环要避免,为什么?
如果你的同事有函数式编程,那么他可能已经熟悉了避免for循环的基本原因:
折叠/映射/过滤器涵盖了列表遍历的大多数用例,并且非常适合函数组合.For循环不是一个好的模式,因为它们不可组合.
大多数情况下,您遍历列表以折叠(聚合),映射或过滤列表中的值.这些高阶函数已经存在于每种主流函数语言中,因此您很少看到函数代码中使用的for循环习语.
高阶函数是函数组合的基础,这意味着您可以轻松地将简单函数组合成更复杂的函数.
为了给出一个非常重要的例子,请用命令式语言考虑以下内容:
let x = someList;
y = []
for x' in x
    y.Add(f x')
z = []
for y' in y
    z.Add(g y')
在函数式语言中,我们编写map g (map f x),或者我们可以使用消除中间列表map (f . g) x.现在我们原则上可以从命令式版本中删除中间列表,这会有所帮助 - 但不会太多.
命令式版本的主要问题是for循环是实现细节.如果你想改变这个功能,你就改变了它的实现 - 你最终修改了很多代码.
一个很好的例子,你怎么写map g (filter f x)命令?好吧,既然你不能重复使用地图和地图的原始代码,你需要编写一个过滤和映射的新函数.如果您有50种映射方式和50种过滤方式,您需要50 ^ 50个函数,或者需要模拟使用命令模式将函数作为第一类参数传递的能力(如果您曾尝试过函数式编程)在Java中,你了解这可能是一场噩梦.
早在功能宇宙中,你可以概括map g (map f x)的方式,可以让你换出map用filter或fold需要:
let apply2 a g b f x = a g (b f x)
并使用称它为apply2 map g filter f或apply2 map g map f或apply2 filter g filter f或任何你需要的.现在你可能永远不会在现实世界中编写类似的代码,你可能会使用以下方法简化它:
let mapmap g f = apply2 map g map f
let mapfilter g f = apply2 map g filter f
高阶函数和函数组合为您提供了使用命令式代码无法获得的抽象级别.
通过抽象循环的实现细节,您可以无缝地将一个循环交换为另一个循环.
请记住,for循环是一个实现细节.如果需要更改实现,则需要更改每个for循环.
映射/折叠/过滤抽象循环.因此,如果要更改循环的实现,可以在这些函数中更改它.
现在你可能想知道为什么你想抽象出一个循环.考虑将项目从一种类型映射到另一种类型的任务:通常,项目一次映射一个,按顺序映射,并独立于所有其他项目映射.大多数情况下,像这样的地图是并行化的主要候选者.
不幸的是,顺序映射和并行映射的实现细节是不可互换的.如果你的代码中有大量的顺序映射,并且你想要将它们换成并行映射,那么你有两种选择:在你的代码库中复制/粘贴相同的并行映射代码,或者将映射逻辑抽象为两个函数map和pmap.一旦你走了第二条路线,你就已经在功能编程方面处于领先地位.
如果您了解函数组合的目的并抽象出实现细节(甚至细节像循环一样简单),您就可以开始了解函数式编程如何以及为何如此强大.
Ree*_*sey 14
比较以下内容:
var collection = GetMyCollection();
for(int i=0;i<collection.Count;++i)
{
     if(collection[i].MyValue == someValue)
          return collection[i];
}
vs foreach:
var collection = GetMyCollection();
foreach(var item in collection)
{
     if(item.MyValue == someValue)
          return item;
}
与LINQ:
var collection = GetMyCollection();
return collection.FirstOrDefault(item => item.MyValue == someValue);
就个人而言,所有三个选项都有它们的位置,我全部使用它们.这是为您的方案使用最合适的选项的问题.
Ian*_*cer 14
for循环没有任何问题,但是这里有一些人们可能更喜欢LINQ这样的函数/声明性方法的原因,你可以在其中声明你想要的东西,而不是你如何得到它:
功能方法可能更容易使用PLINQ或编译器手动并行化.随着CPU迁移到更多核心,这可能变得更加重要.
功能方法使得在多步骤过程中更容易实现延迟评估,因为您可以将中间结果作为一个尚未完全评估的简单变量传递到下一步,而不是完全评估第一步,然后将集合传递给下一步(或不使用单独的方法和yield语句来实现相同的程序).
功能方法通常更短,更容易阅读.
功能方法通常会消除for循环中的复杂条件体(例如if语句和'continue'语句),因为您可以将for循环分解为逻辑步骤 - 选择匹配的所有元素,对它们执行操作,...
对于循环不会杀死人(或小猫,或小狗,或tribbles).人们杀人.对于循环,它本身也不错.然而,就像其他任何事情一样,你使用它们的方式可能很糟糕.
小智 6
有时你不会只杀死一只小猫.
for(int i = 0; i <kittens.Length; i ++){kittens [i] .Kill(); }
有时候你会全部杀掉他们.
您可以很好地重构代码,这样您就不会经常看到它们.一个好的函数名称肯定比for循环更具可读性.
以AndyC为例:
环
// mystrings is a string array
List<string> myList = new List<string>();
foreach(string s in mystrings)
{
    if(s.Length > 5)
    {
        myList.add(s);
    }
}
LINQ
// mystrings is a string array
List<string> myList = mystrings.Where<string>(t => t.Length > 5)
                               .ToList<string();
当您在功能中使用第一个或第二个版本时,它更容易阅读
var filteredList = myList.GetStringLongerThan(5);
现在这是一个非常简单的例子,但你明白我的观点.