c# 扩展方法奇思妙用高级篇二:Aggregate 扩展及其改进

Enumerable.Aggregate 扩展方法在System.Linq命名空间中,是Enumerable类的第一个方法(按字母顺序排名),但确是Enumerable里面相对复杂的方法。
MSDN对它的说明是:对序列应用累加器函数。备注中还有一些说明,大意是这个方法比较复杂,一般情况下用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不再需要额外参数,而Aggregate确还要将一个lambda作为参数。因为用起来麻烦,操作太低级,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);
        }

输出结果是:Able was I ere I saw Elba (注:出自《大国崛起》,狄娜最后讲述了拿破仑一句经典)。
当然考虑性能的话还是用StringBuilder吧,这里主要介绍用法。这个Sum做不到吧!

Aggregate还可以将所有字符串倒序累加,配合String.Reverse扩展可以实现整个句子的倒序输出:

复制代码
         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);
        }
复制代码

看下面,输出结果好像不太对:

怎么中间的都一样,两的单词首尾字母大小写发生转换了呢?!
仔细看看吧,不是算法有问题,是输入“有问题”。搜索一下“Able was I ere I saw Elba”,这可是很有名的英文句子噢!

Aggregate还可以实现异或(^)操作

         public   static   void  Test4()
        {
            
byte [] data  =   new   byte [] {  0x31 0x32 0x33 0x34 0x35  };
            
byte  checkSum  =  data.Aggregate((a, n)  =>  ( byte )(a  ^  n));
        }

对经常作串口通信的朋友比较实用。

看来Aggregate也是比较“简单易用”的,深入一步来看看它是怎么实现的吧,使用Reflector,反编译一下System.Core.dll。
以下代码取自反编译结果,为了演示删除了其中的空值判断代码:

Aggregate

也很简单吧,就是一个循环!前面lambda表达式中参数a, n 分别对应current, enumerator.Current,对照一下,还是很好理解的。

现在我们想求整数数组中位置为偶数的数的和(间隔求和),可以用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扩展设计的很好,它不但能带出某项的值“n”,还能带出项的位置“i”。
Aggregate可不行!我们来改进一下:

复制代码
         // 改进的Aggerate扩展(示例代码,实际使用请添加空值检查)
         public   static  TSource Aggregate < TSource > ( this  IEnumerable < TSource >  source, Func < TSource, TSource,  int , TSource >  func)
        {
            
int  index  =   0 ;
            
using  (IEnumerator < TSource >  enumerator  =  source.GetEnumerator())
            {
                enumerator.MoveNext();
                index
++ ;
                TSource current 
=  enumerator.Current;
                
while  (enumerator.MoveNext())
                    current 
=  func(current, enumerator.Current, index ++ );
                
return  current;
            }
        }
复制代码

改进后的Aggregate更加强大,前面的求偶数位置数和的算法可以写成下面的样子:

         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
        }

可能不够简洁,但它一个函数代替了Where和Sum。所在位置“i“的引入给Aggregate带来了很多新的活力,也增加了它的应用范围!

我随笔《使用“初中知识”实现查找重复最优算法 + 最终极限算法》中最后提出的“最终极限算法”,用上这里改进的Aggregate扩展,也可以甩开Select和Sum,更加精简一步了: 

复制代码
         public   static   void  Test7()
        {
            
// 1~n放在含有n+1个元素的数组中,只有唯一的一个元素值重复,最简算法找出重复的数
             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#扩展方法奇思妙用》,敬请关注!

转载于:https://www.cnblogs.com/ywsoftware/archive/2013/06/09/3128764.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值