C#擴展方法之Enumerable.Aggregate分析
Enumerable.Aggregate 擴展方法在System.Linq命名空間中,是Enumerable類的***個方法(按字母順序排名),但確是Enumerable里面相對復雜的方法。
MSDN對這個C#擴展方法的說明是:對序列應用累加器函數(shù)。備注中還有一些說明,大意是這個方法比較復雜,一般情況下用Sum、Max、Min、Average就可以了。
看看下面的代碼,有了Sum,誰還會用Aggregate呢!
- public static void Test1()
- {
- int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
- int sum1 = nums.Sum();
- int sum2 = nums.Aggregate((i,j)=>i+j);
- }
同是求和,Sum不再需要額外參數(shù),而Aggregate確還要將一個lambda作為參數(shù)。因為用起來麻煩,操作太低級,Aggregate漸漸被大多人忽視了...
實際上Aggregate因為“低級”,功能確是很強大的,通過它可以簡化很多聚合運算。
首先來看對Aggregate組裝字符串的問題:
- public static void Test2()
- {
- string[] words = new string[] { "Able", "was", "I", "ere", "I", "saw", "Elba"};
- string s = words.Aggregate((a, n) => a + " " + n);
- Console.WriteLine(s);
- }
輸出結(jié)果是:Able was I ere I saw Elba (注:出自《大國崛起》,狄娜***講述了拿破侖一句經(jīng)典)。
當然考慮性能的話還是用StringBuilder吧,這里主要介紹用法。這個Sum做不到吧!
Aggregate還可以將所有字符串倒序累加,配合String.Reverse擴展可以實現(xiàn)整個句子的倒序輸出:
- public static void Test3()
- {
- string[] words = new string[] { "Able", "was", "I", "ere", "I", "saw", "Elba"};
- string normal = words.Aggregate((a, n) => a + " " + n);
- string reverse = words.Aggregate((a, n) => n.Reverse() + " " + a);
- Console.WriteLine("正常:" + normal);
- Console.WriteLine("倒置:" + reverse);
- }
- // 倒置字符串,輸入"abcd123",返回"321dcba"
- public static string Reverse(this string value)
- {
- char[] input = value.ToCharArray();
- char[] output = new char[value.Length];
- for (int i = 0; i < input.Length; i++)
- output[input.Length - 1 - i] = input[i];
- return new string(output);
- }
看下面,輸出結(jié)果好像不太對:
怎么中間的都一樣,兩的單詞首尾字母大小寫發(fā)生轉(zhuǎn)換了呢?!
仔細看看吧,不是算法有問題,是輸入“有問題”。搜索一下“Able was I ere I saw Elba”,這可是很有名的英文句子噢!
Enumerable.Aggregate這個C#擴展方法還可以實現(xiàn)異或(^)操作:
- public static void Test4()
- {
- byte[] data = new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35 };
- byte checkSum = data.Aggregate((a, n) => (byte)(a ^ n));
- }
對經(jīng)常作串口通信的朋友比較實用。
看來Aggregate也是比較“簡單易用”的,深入一步來看看它是怎么實現(xiàn)的吧,使用Reflector,反編譯一下System.Core.dll。
以下代碼取自反編譯結(jié)果,為了演示刪除了其中的空值判斷代碼:
- public static TSource Aggregate
( this IEnumerablesource, Func func) - {
- using (IEnumerator
enumerator = source.GetEnumerator()) - {
- enumerator.MoveNext();
- TSource current = enumerator.Current;
- while (enumerator.MoveNext())
- current = func(current, enumerator.Current);
- return current;
- }
- }
也很簡單吧,就是一個循環(huán)!前面lambda表達式中參數(shù)a, n 分別對應current, enumerator.Current,對照一下,還是很好理解的。
現(xiàn)在我們想求整數(shù)數(shù)組中位置為偶數(shù)的數(shù)的和(間隔求和),可以用Where配合Sum:
- public static void Test5()
- {
- int[] nums = new int[] { 10, 20, 30, 40, 50 };
- int sum1 = nums.Where((n, i) => i % 2 == 0).Sum();//10 + 30 + 50
- }
這個Where擴展設(shè)計的很好,它不但能帶出某項的值“n”,還能帶出項的位置“i”。
Aggregate可不行!我們來改進一下:
- //改進的Aggerate擴展(示例代碼,實際使用請?zhí)砑涌罩禉z查)
- public static TSource Aggregate
( this IEnumerablesource, Func int, TSource> func) - {
- int index = 0;
- using (IEnumerator
enumerator = source.GetEnumerator()) - {
- enumerator.MoveNext();
- index++;
- TSource current = enumerator.Current;
- while (enumerator.MoveNext())
- current = func(current, enumerator.Current, index++);
- return current;
- }
- }
改進后的Aggregate更加強大,前面的求偶數(shù)位置數(shù)和的算法可以寫成下面的樣子:
- public static void Test6()
- {
- int[] nums = new int[] { 10, 20, 30, 40, 50 };
- int sum2 = nums.Aggregate((a, c, i) => a + i%2 == 0 ? c : 0 );//10 + 30 + 50
- }
可能不夠簡潔,但它一個函數(shù)代替了Where和Sum。所在位置“i“的引入給Aggregate帶來了很多新的活力,也增加了它的應用范圍!
我隨筆《使用“初中知識”實現(xiàn)查找重復***算法 + 最***限算法》中***提出的“最***限算法”,用上這里改進的Aggregate擴展,也可以甩開Select和Sum,更加精簡一步了:
- public static void Test7()
- {
- //1~n放在含有n+1個元素的數(shù)組中,只有唯一的一個元素值重復,最簡算法找出重復的數(shù)
- int[] array = new int[] { 1, 3, 2, 3, 4, 5 };
- //原極限算法
- int repeatedNum1 = array.Select((i, j) => i - j).Sum();
- //***極限算法
- int repeatedNum2 = array.Aggregate((a, n, i) => a + n - i);
- }
以上就介紹了C#擴展方法之Enumerable.Aggregate。本文來自鶴沖天的博客:《c#擴展方法奇思妙用高級篇二:Aggregate擴展其改進》。
【編輯推薦】