posts - 165,  comments - 881,  trackbacks - 42
      出于对Lambda的好奇所以学习了一下,看能不能通过Lambda来开启新的更用效的代码编写方式。以下是通过Lambda实现一个通用型的递归控制处理函数。这只是一个参考,实际上你可以挖掘Lambda更多的使用方法.多谢 装配脑袋  指出问题重新调整一下代码(这里本意不是描述该方法功能如何,而是想体现Lambda的引发的虽一种编码方式).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
namespace lambdaTest
{
    
public delegate void EventExecute<T>(T source, Program.Recursive<T> next);
    
public static class Program
    
{
        
static void Main(string[] args)
        
{

            UnderList ul 
= new UnderList();
            ul.EmployeeID 
= 2;
            
string sql = "select EmployeeID,FirstName from Employees where ReportsTo={0}";
            
using (System.Data.SqlClient.SqlConnection conn
                
= new System.Data.SqlClient.SqlConnection("Data Source=.;Initial Catalog=Northwind;Integrated Security=True"))
            
{
                conn.Open();
                Do
<UnderList>(ul, p => (p.EmployeeID > 0),
                   
delegate(UnderList source, Program.Recursive<UnderList> r)
                   
{
                       source.Format 
+= "\t";
                       List
<int> mUnders = new List<int>();
                       SqlCommand cmd 
= conn.CreateCommand();
                       cmd.CommandText 
= string.Format(sql, source.EmployeeID);
                       
using (SqlDataReader reader = cmd.ExecuteReader())
                       
{
                           
while (reader.Read())
                           
{
                               mUnders.Add((
int)reader[0]);
                               Console.WriteLine(source.Format 
+ reader[1]);

                           }


                       }

                       source.EmployeeID 
= 0;
                       
foreach (int item in mUnders)
                       
{
                           source.EmployeeID 
= item;
                           r.Do();

                       }

                   }

                );
            }

     
           Console.Read();
            
            
         }

        
private static void Do<T>(T i, Func<T, bool> a, EventExecute<T> e)
        
{

            Program.Recursive
<T> rec = new Recursive<T>(i,e,a);
            e(i, rec);
            
        }

        
public class Recursive<T>
        
{
            
public Recursive(T source, EventExecute<T> e, Func<T, bool> expression)
            
{
                Source 
= source;
                E 
= e;
                Expression 
= expression;
            }

            
private T Source;
            
private EventExecute<T> E;
            Func
<T, bool> Expression;
            
            
public void Do()
            
{
                
if (Expression(Source))
                    E(Source, 
this);
            }

            
           

        }

    }

    
public class UnderList
    
{
        
private int mEmployeeID;
        
public int EmployeeID
        
{
            
get
            
{
                
return mEmployeeID;
            }

            
set
            
{
                mEmployeeID 
= value;
            }

        }

        
public string Format = "";
        
        

    }

}

posted on 2007-12-14 11:13 henry 阅读(2342) 评论(27)  编辑 收藏 网摘

FeedBack:
2007-12-14 11:18 | Autumoon      
Lambda表达式在.NET里面来说是新的,但在别的编程语言来说,未必是新的。
  回复  引用  查看    
#2楼 [楼主]
2007-12-14 11:22 | henry      
@Autumoon
抱歉,我一直当这里是.net的社区...
  回复  引用  查看    
2007-12-14 11:35 | 木野狐(Neil Chen)      
你这代码无法编译啊,

Add(i, a, execute);

这里是执行什么?
  回复  引用  查看    
#4楼 [楼主]
2007-12-14 11:41 | henry      
@木野狐(Neil Chen)
抱歉...原代码是没调整好的.现在是好的
  回复  引用  查看    
2007-12-14 11:42 | 装配脑袋      
不懂什么叫“通用型的递归控制处理函数”……非广为接受的术语应该先介绍下
  回复  引用  查看    
2007-12-14 11:46 | 装配脑袋      
原来是尾递归当成for来使用……汗,这个在.NET世界中应该没有太大的利用价值。
  回复  引用  查看    
#7楼 [楼主]
2007-12-14 11:50 | henry      
@装配脑袋
private static void Recursive<T>(T i, Func<T, bool> a, EventExecute<T> execute)
{
execute(i);
if (a(i))
return;
Recursive(i, a, execute);
}
看这个代码更直接易懂,其实用一个函数来控制递归
  回复  引用  查看    
#8楼 [楼主]
2007-12-14 11:51 | henry      
@装配脑袋
...价值并不是在事例,只是说明Lambda除在我们常用的linq外还可以在别的场所发挥作用.
发挥一下T,就不是递归当成for来使用这么简单了
  回复  引用  查看    
2007-12-14 12:11 | 装配脑袋      

你这里并没有“递归”啊,那种写法是典型的迭代算法嘛。如果要实现Lambda递归,可以事先实现一个递归版的不动点:

public static Func<TA, TR> Fix<TA, TR>(Func<Func<TA, TR>, Func<TA, TR>> f)
{
    
return t => f(Fix(f))(t);
}
然后你就可以递归了:
static void Main(string[] args)
{
    Func
<Test, Test> CountFive = Fix<Test, Test>(f => t =>
    {
        t.Count
++;
        Console.WriteLine(t.Count.ToString());
        
return (t.Count >= 5? t : f(t);
    });

    CountFive(
new Test());
}

  回复  引用  查看    
2007-12-14 12:12 | 天方      
函数式编程并不是什么新概念。
另外,递归的优点本身就是清晰、简洁。你这里使用lambda来封装让人看着实在犯晕。
  回复  引用  查看    
#11楼 [楼主]
2007-12-14 13:09 | henry      
@装配脑袋
你说得很对,我一时糊涂了:)
  回复  引用  查看    
2007-12-14 16:31 | STS [未注册用户]
这个和Lambda没什么关系吧.
如果不是编译成Expression,
Lambda只是一种更简单的写法.
而不是必须的.
不是它出现了才能有某些应用.

其实用匿名函数就能做递归了.
例如

class Program
{
static void Main(string[] args)
{
for (int i = 1; i < 20; i++)
{
Console.WriteLine(Feb(i));
}


}
static int Feb(int x)
{
int t = 0;
Action<int> func = null;
func = delegate(int n)
{
if (n >= 2)
{
func(n - 1);
func(n - 2);
}
else
{
t += n;
}
};
func(x);
return t;
}
}

当然这个是一个简单的例子.
如果某些情况需要维护一些context,又不想创建一个新的class来储存.
那么匿名函数就是非常好的选择.



  回复  引用    
2007-12-14 16:50 | BirdsHover      
还是喜欢传统的方式,Lambda会有点绕,虽然简单了点,不过很象
int a = 1==0 ? 1:0;
这种
  回复  引用  查看    
2007-12-14 17:31 | 周银辉      
不知是我们没有太注意还是怎么的,写出来的Lambda像是给机器读的,编码上感觉很杂糅而不规范
  回复  引用  查看    
#15楼 [楼主]
2007-12-14 17:47 | henry      
@周银辉
其实并不是这么复杂
Do<T>(T, T => (T.EmployeeID > 0),delegate(T source, Program.Recursive<T> r)
{

});
递归所需要对象,递归条件,递归最终处理的方法
  回复  引用  查看    
2007-12-14 17:50 | BirdsHover      
怎么越看越象C++啊....
  回复  引用  查看    
2007-12-14 19:00 | 装配脑袋      

@STS

如果有Lambda,你的Feb可以写成这样

var feb = Fix<intint>(f => x => x > 2 ? f(x - 1+ f(x - 2) : 1);

即使是简化,这也是相当吸引人的
  回复  引用  查看    
2007-12-14 19:59 | STS [未注册用户]
@装配脑袋 :
不知道你的Fix是不是类似这样的:
static Func<X, Y> Fix<X, Y>(Func<Func<X, Y>, Func<X, Y>> func)
{
Func<X, Y> f = null;
f = func(x => f(x));
return f;
}

实际上和我的那个还是同样的写法.
只不过是在某些特定的情况下,Lambda可以写短些. 但是会更加难阅读.

如过不考虑context,匿名函数也是OK的.
Func<int, int> Feb = null;
Feb = delegate(int n) { return n > 2 ? Feb(n - 1) + Feb(n - 2) : 1; };

而我那个写法,演示了t这个context对象. (注意那个写法不是返回t,而是修改t)
这个Lambda就不能直接做到了.



  回复  引用    
2007-12-14 20:22 | 装配脑袋      

@STS

Fix定义见9楼。其实要不要把代码写成两行,就是我这个Fix决定性的地方,呵呵。我可以直接将feb的定义传递给接受Func<int, int>的地方,而不用变量承载,而你必须要定义一个变量,相当于必须给函数起名,不能做到匿名。这就是真正的函数式编程效果(当然这不是Lambda的功劳,而是Fix的)。

Fix还存在一个自身纯匿名的定义,用匿名方法写比Lambda复杂得多哦

'注,此乃VB语法
Fix = Function(f) _
      (
Function(h) Function(x) f(h(h))(x)) _
      (
Function(h) Function(x) f(h(h))(x))

PS.C#不存在任何匿名方法能写,Lambda不能写的程序,包括你的例子


  回复  引用  查看    
2007-12-14 21:26 | STS [未注册用户]
看了你的代码才知道Lambda表达式是支持多行代码的.
这个情况下,匿名代码能写的, Lambda当然也能写.
因为这种连多条statement都能写的Lambda,
已经彻底是匿名方法的简写版了.
或者说,要讨论的话题已经模糊了.
到底是Lambda表达式写法是否优越的讨论,
还是函数式编程的讨论.还是语言写法的讨论???

要知道f => x => x > 2 ? f(x - 1) + f(x - 2) : 1 这里,
f 不是 "x => x > 2 ? f(x - 1) + f(x - 2) : 1".
这个写法本身就不是递归.
只不过是在Fix中采取特殊方法,
生成一个f去调用"x => x > 2 ? f(x - 1) + f(x - 2) : 1"
产生递归的效果.

所以这个写法和下面的做法完全不同:
Func<int, int> Feb = null;
Feb = delegate(int n)
{
if (n > 2)
return Feb(n - 1) + Feb(n - 2);
return 1;
};
或者换成Lambda写法
Func<int, int> Feb = null;
Feb = n => n > 2 ? Feb(n - 1) + Feb(n - 2) : 1;

可以看出.这个才是真正的递归.

其实定义 Func<int, int> Feb = null; 是迫不得已的.
直接写 Func<int, int> Feb = n => n > 2 ? Feb(n - 1) + Feb(n - 2) : 1;
的话, C#会提示Feb是未赋值的.


我已经不知道自己想讨论什么了.
只希望把目前还比较清晰的想法写出来看看.

另外附带一个匿名函数递归用法的例子:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
List<string> errorlist = new List<string>();

Action<string> ScanDirectory = null;
ScanDirectory = delegate(string dir)
{
list.Add(dir);
string[] subdirs;
try
{
subdirs = Directory.GetDirectories(dir);
}
catch (UnauthorizedAccessException)
{
errorlist.Add(dir);
return;
}
foreach (string subdir in subdirs)
{
ScanDirectory(subdir);
}
};

string root = "C:\\Program Files\\Common Files\\Microsoft Shared";
ScanDirectory(root);

foreach (string dir in list)
{
Console.WriteLine(dir.Remove(0, root.Length));
}

Console.WriteLine(list.Count + " , " + errorlist.Count);
}
}
}

  回复  引用    
2007-12-14 22:13 | pk的眼泪      
看的头晕.
  回复  引用  查看    
2007-12-14 22:55 | 装配脑袋      
@STS

Func<int, int> Feb = null;
Feb = n => n > 2 ? Feb(n - 1) + Feb(n - 2) : 1;

这里为什么不是匿名的,因为你的函数 Lambda n 捕捉了Feb这个变量,于是才实现递归的。函数里的Feb因为延迟调用所以才可以在获得定义前被捕捉,之后在获得定义后进行调用。这是传统意义上的递归,也就是说通过名字引用自身的递归。而递归一词,只说函数要调用自身,没说非要通过一个名字调用自身,后者只是命令式的实现方法而已。

我就是用Lambda和Fix实现了递归算法。虽然我写起来还是匿名的,但是运行起来之后它就是调用了自己,这是个关键的地方。而且我给出了Fix的纯匿名定义(VB版)。就是说使用Fix可以完全匿名地实现递归算法。


  回复  引用  查看    
2007-12-14 23:02 | 装配脑袋      
PS.回到主题,我为什么认为Lambda比匿名方法重要,就是因为写法简单。别小看这个优势,看Fix的VB定义,用的是Lambda的语法实现的。事实上我尝试写它的时候一下子就写成功了!而原先在C# 2.0中我也尝试实现过Fix,而我没有成功,因为代码一下子就复杂的超过理解极限。Lambda简化语法就是让我们不再思考函数语义,而是直接用函数式语言的方式思考和理解问题,这样我们就能想出更多应用。
就好像现在的编程语言也不过是汇编的简化,但是完全用汇编写程序脑子里就无暇顾及什么递归、对象之类的概念了,因为实现最基本运算已经耗费了我们的脑子。使用现代语言能够使我们有机会注意更高抽象层次的东西。
  回复  引用  查看    
2007-12-15 06:17 | 怪怪      
@装配脑袋
每次看你写的这些我就得想两分钟, 看来脑子笨就是没辙.. :{
  回复  引用  查看    
2007-12-15 14:56 | Enzo      
@怪怪
@装配脑袋 在这方面深有研究,学习!
  回复  引用  查看    
2008-01-07 21:22 | BirdsHover      
装配的脑袋就是好使啊,有时间最好整出个Lambda各种情况下代码工作的流程出来,我还是习惯按流程理解问题。多点解释,目前还没看全面的这方面资料,有些东西还是不解
  回复  引用  查看    
2008-07-12 21:10 | 蛙蛙池塘      
呵呵,装配脑袋我一直以为只会写VB的示例代码呢。
  回复  引用  查看    

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2007-12-14 15:17 编辑过
"五向定位"职业成长路线公开课(上海、南京、大连)
Google站内搜索


相关链接:
 




<2008年7月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

寻求伯乐,限广州地区有意联系


与我联系

搜索

 

常用链接

留言簿(21)

我参加的小组

我的标签

随笔分类

最新评论