智能循环:提升代码的可读性与效率

在编程中,循环是一个基本且频繁使用的构造。然而,大多数情况下,只是停留在使用标准的for、foreach、while循环。实际上,可以做得更智能。在许多情况下,会在循环中添加额外的信息,例如检查索引是偶数还是奇数,或者检查当前值是否是第一个或最后一个。

更智能的循环

非常喜欢lambda表达式——事实上,可能有点过于喜欢了。能够像参数一样传递委托确实可能会被滥用,但在某些情况下,它可以使代码更加优雅。让来看一些可以改进数组操作的代码。

以下是一个C#的扩展方法示例,它提供了一种在循环中使用额外信息的方式:

namespace LoopExtensions { public static class LoopExtensionMethods { public static IEnumerable Each(this IEnumerable collection, Action> each) { return LoopExtensionMethods.Each(collection, 0, collection.Count(), each); } public static IEnumerable Each(this IEnumerable collection, int start, Action> each) { return LoopExtensionMethods.Each(collection, start, collection.Count(), each); } public static IEnumerable Each(this IEnumerable collection, int start, int end, Action> each) { Action, T> handle = (detail, item) => { each(detail); }; return LoopExtensionMethods.Each(collection, start, end, handle); } public static IEnumerable Each(this IEnumerable collection, Action, T> each) { return LoopExtensionMethods.Each(collection, 0, collection.Count(), each); } public static IEnumerable Each(this IEnumerable collection, int start, Action, T> each) { return LoopExtensionMethods.Each(collection, start, collection.Count(), each); } public static IEnumerable Each(this IEnumerable collection, int start, int end, Action, T> each) { if (start < 0 || end > collection.Count()) { throw new ArgumentOutOfRangeException(); } foreach (T value in collection) { each(new ElementDetail(value, start++, end, collection), value); if (start == end) { break; } } return collection; } } public class ElementDetail { internal ElementDetail(T value, int index, int total, IEnumerable collection) { this.Value = value; this.Index = index; this.Total = total; this.Collection = collection; } public int Index { get; private set; } public int Total { get; private set; } public T Value { get; private set; } public IEnumerable Collection { get; private set; } public T Previous { get { return !this.First ? this.Collection.ElementAt(this.Index - 1) : default(T); } } public T Next { get { return !this.Last ? this.Collection.ElementAt(this.Index + 1) : default(T); } } public bool Last { get { return this.Index == (this.Total - 1); } } public bool First { get { return this.Index == 0; } } public bool Outer { get { return this.First || this.Last; } } public bool Inner { get { return !this.Outer; } } public bool Even { get { return this.Index % 2 == 0; } } public bool Odd { get { return !this.Even; } } public int StepNumber { get { return this.Index + 1; } } public float PercentCompleted { get { return ((float)this.Index / (float)this.Total) * 100; } } public float PercentRemaining { get { return 100 - this.PercentCompleted; } } public int StepsCompleted { get { return this.Index; } } public int StepsRemaining { get { return this.Total - this.Index; } } } }

这段代码虽然很长,但它是一组扩展方法,可以与IEnumerable一起使用。基本思想是可以在委托中执行循环,该委托接受有关循环中元素的额外信息。

下面是一个快速示例:

string[] items = { "Apple", "Orange", "Grape", "Watermellon", "Kiwi" }; items.Each((item) => { Console.Write("{0} > ", item.Inner ? "Inner" : "Outer"); if (!item.First) { Console.Write("Previous: {0}, ", item.Previous); } Console.Write("Current: {0} ({1})", item.Value, item.StepNumber); if (!item.Last) { Console.Write(", Next: {0}", item.Next); } Console.WriteLine("-- {0}% remaining", item.PercentRemaining); });

输出:

Outer > Current: Apple (1), Next: Orange -- 100% remaining Inner > Previous: Apple, Current: Orange (2), Next: Grape -- 80% remaining Inner > Previous: Orange, Current: Grape (3), Next: Watermelon -- 60% remaining Inner > Previous: Grape, Current: Watermellon (4), Next: Kiwi -- 40% remaining Outer > Previous: Watermelon, Current: Kiwi (5) -- 20% remaining

通常情况下,会在循环中编写所有的比较,然后根据需要使用它们。相反,在这段代码中,传入了一个额外的参数,其中包含了许多可能在循环中找到的常用比较的快捷方式。通过这样做,提高了可读性,专注于循环正在做的事情。

旧方法有什么问题吗?

没有!内联编写比较可能是有利的,因为没有做任何不需要的工作。然而,在某些情况下,提高的可读性会使代码质量大不相同。考虑以下两段MVC代码,并决定哪一段更容易阅读:

ASP.NET <ul> <% foreach (SiteMapNode node in breadcrumb) { %> <li class="item<%=(breadcrumb.IndexOf(node) % 2 == 0 ? "item-even" : "item-odd") %>"> <% if (breadcrumb.First().Equals(node) || breadcrumb.Last().Equals(node)) { %> <strong> <% } %> <a href="<%= node.Url %>"> ( <%= (breadcrumb.IndexOf(node) + 1) %> ) : <%= node.Text %> </a> <% if (breadcrumb.First().Equals(node) || breadcrumb.Last().Equals(node)) { %> </strong> <% } %> </li> <% } %> </ul>

或者使用循环助手的相同代码…

ASP.NET <ul> <% breadcrumb.Each((node) => { %> <li class="item<%=(node.Even ? "item-even" : "item-odd") %>"> <% if (node.Outer) { %> <strong> <% } %> <a href="<%= node.Value.Url %>"> ( <%= node.StepNumber %> ) : <%= node.Value.Text %> </a> <% if (node.Outer) { %> </strong> <% } %> </li> <% }); %> </ul>

这个示例仅使用了ElementDetail类,但当然,也可以使用其他扩展方法,它还单独提供值。

为人类编写代码,而不是为计算机

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485