如何在C#中用函数语句替换for循环?
一位同事曾经说过,每次我写一个for循环,上帝都在杀死一只小猫 当被问及如何避免for循环时,他的答案是使用函数式语言。但是,如果您使用的是非函数式语言,比如C#,那么有哪些技术可以避免循环或通过重构来消除循环?也许是lambda表达式和LINQ?如果是,怎么做 问题 因此,问题归结为:如何在C#中用函数语句替换for循环?,c#,linq,functional-programming,lambda,for-loop,C#,Linq,Functional Programming,Lambda,For Loop,一位同事曾经说过,每次我写一个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();
在我的书中,第二个看起来更加整洁和简单,尽管第一个没有什么问题
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中,您声明了自己想要的东西,而不是如何获得它:-
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);
}
}