c#扩展方法奇思妙用变态篇一:由Fibonacci数列引出“委托扩展”及“递推递归委托”

 

先回顾一个数列的概念:按一定次序排列的一列 数 称为数列...(请参见百度百科:数列)
几个简单的数列:
      1, 1, 1, 1, 1, 1, 1...                //数列1
      0, 1, 2, 3, 4, 5, 6, 7...                //数列2
      0, 1, 4, 9, 16, 25, 36, 49...        //数列3
通项公式的定义:数列的第n项与项的序数这间的关系,也就是数列生成算法
上面几个数列可表示为
       An = F(n) = 1
       An = F(n) = n
       An = F(n) = n * n

有了数列和通项公式的定义,我们的任务就好描述了:
      用最简洁的代码描述通项公式,用最简洁算法生成数列的前N个数。

在此要求下,用常规代码是做不到简洁的,这里我们用lambda表达式描述通项公式:

         // 数列1 通项公式
         public   static  Func < int int >  fun1  =  n  =>   1 ;
        
// 数列2 通项公式
         public   static  Func < int int >  fun2  =  n  =>  n;
        
// 数列3 通项公式
         public   static  Func < int int >  fun3  =  n  =>  n  *  n;

 

lambda表达式是不是与数学公式很像啊!

再来看生成算法,这里用了一个不一般的扩展:

         /// <summary>
        
/// 生成队列的前count项
        
/// </summary>
        
/// <param name="func">通项公式</param>
        
/// <param name="count">生成的数量</param>
        
/// <returns>队列前count项</returns>

         public   static  IEnumerable < int >  GetSequence( this  Func < int int >  func,  int  count)
        
{
            
for (int i = 0; i < count; i++yield return func(i);
        }

 

相信大家见的扩展大多针对类(object, string)、接口(IEnumerable<T>)进行扩展,针对Func(委托)估计对大多数人来说都是第一次。
这个扩展就是标题中说的“委托扩展”,感觉很怪吧,很别扭吧,很别管太多,看看怎么调用吧:

         public   static   void  Test1()
        {
            
int [] ints1  =  fun1.GetSequence( 10 ).ToArray();       // 1, 1, 1, 1
             int [] ints2  =  fun2.GetSequence( 10 ).ToArray();       // 0, 1, 2, 3
             int [] ints3  =  fun3.GetSequence( 10 ).ToArray(); ;     // 0, 1, 4, 9
        }

自我感觉比较简洁,而且将生成数列(GetSequence)与数列算法(通项公式)分开,也达到了生成数列(GetSequence)的复用。

上面几个数列比较简单,现在来看Fibonacci,
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55...
用图形表示如下:

这个序列在大家学习c语言递推递归时都接触过,这个序列很神奇,请参看维基百科:斐波那契数列
它的通项公式是 An = F(n) = n                          n =0, 1
                                       F(n-1) + F(n-2)      n>1
注意:关于这数列有的是从n从0开始,有的是从1开始,这里不计较。

递推递归算法如下,容易理解效率确很低!!

         public   static   int  GetFibonacci( int  n)
        {
            
if  (n  >   1 return  GetFibonacci(n  -   1 +  GetFibonacci(n  -   2 );
            
else   return  n;
        }

本文是为了引出递推递归委托,暂不是算法的效率
下面就要大(改)变(形)态了。

不考虑 <1 的情况

         public   static  Func < int int >  Fibonacci  =  n  =>  Fibonacci(n  -   1 +  Fibonacci(n  -   2 );

与数学通项式对比一下,何其相似!这就是我们的“递推递归委托”!

考虑所有情况,完成Fibonacci,如下

         public   static  Func < int int >  Fibonacci  =  n  =>  n  >   1   ?  Fibonacci(n  -   1 +  Fibonacci(n  -   2 ) : n;

实在感叹c#精简的语法,一句代码可以表示一个递推递归!
调用测试下吧!

         public   static   void  Test2()
        {  
            
// 委托扩展方法 + 递推递归委托
             int [] fibonacciSequence  =  Fibonacci.GetSequence( 12 ).ToArray();
        }

当然这个生成算法效率不是一般的低!

最后给出一个数学推导出的精确算法

         public   static  Func < int int >  Fibonacci2  =  n  =>  ( int )( 5.0 .Pow( - 0.5 *  (( 0.5   *  ( 1   +   5.0 .Pow( 0.5 ))).Pow(n  +   1 -  ( 0.5   *  ( 1   -   5.0 .Pow( 0.5 ))).Pow(n  +   1 )));
        
// Pow扩展,简化调用
         public   static   double  Pow( this   double  x,  double  y)
        {
            
return  Math.Pow(x, y);
        }


一点意见:像这样代码,最好是给封装起来,否则会很麻烦的。
这篇文章是给极少数人看的(启发一下),看完后封装好给大多数人用。这是也“变态篇”系列文章的宗旨.
希望大家对 “委托扩展” 和 “递推递归委托”提些看法,名字定义不太好,请指正!

---------------------------------------------------------------------------------------------------------- 
以下为 2009年8月10日20时52分 追加内容
----------------------------------------------------------------------------------------------------------

看了大家的回复,对我鼓励很大,尤其是 装配脑袋 给出的解法(在#7楼)给了我很大的启发,于是我也忍不住把 装配脑袋 的算法改进了一下:

public static Func<Point, Point> f = p => { p.X += p.Y; p.Y = p.X - p.Y; return  p;  }; 
public static Func<Point, Func<Point, Point>int, Point> g1 = (p, f, n) =>  n > 0 ? g1(f(p), f, n - 1
) : p;
public static Func<Point, Func<Point, Point>int, Point> g2 = (p, f, n) =>  n > 0 ? f(g2(p, f, n - 1)) : new Point(01
); 

public static void
 Test8()
{
    
//测试 generate1

    Point p1 = g1(new Point(01), f, 3 );
    
for (int i = 0; i < 12; i++
)
        Console.WriteLine(g1(
new Point(01
), f, i));
    
//测试 generate2

    Point p2 = g2(default(Point), f, 3 );
    
for (int i = 0; i < 12; i++
)
        Console.WriteLine(g2(
default
(Point), f, i));
}

这里用到Point (System.Drawing中的),因为它能包含两个整数,Fibonacci又是前两项之和,所以...
以下是调试运行的结果:

输出

两列Fibonacci,不过第二列刚开始不对。
g1和g2是两种算法,看上去很相似,有什么不同呢,设个断点单步调试(F11)下吧!
上面的代码还不够简洁,最后将f与g1合在一起,如下:

public   static  Func < Point,  int , Point >  g3  =  (p, n)  =>  n  >   0   ?  g3( new  Point(p.X  +  p.Y, p.X), n  -   1 ) : p;

测试调用代码如下:


        public static void Test9()
        
{
            
//测试 generate3
            Point p3 = g3(new Point(01), 3);
            
for (int i = 0; i < 12; i++)
                Console.WriteLine(g3(
new Point(1,0), i));
        }

几种算法的优点和缺点大家来评判吧!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值