函数式编程中比较有代表性的特点如:高阶函数(函数做为函数的参数),组合函数,纯函数缓存等。
F#就是一门函数式编程语言,但是C#是一门指令性的语言,并不包含全部函数式编程的全部特征。
用C#实现函数式编程,利用到了C#各种新增特性,如lambda表达式、表达式树、LINQ、扩展方法等等。
因此这个话题一方面是深入理解C#语言的一个途径,另一方面来说,一种新的编程模式本身就是非常有趣也有能吊起人味口的东西,因此,当年函数式编程也让勇哥痴迷了一把。
下面我把当初学习时的实验代码发上来,这些实验代码只包含了部分函数编程的知识,当年勇哥也是学到半途而废了呀,汗!
如果有可能,勇哥也想把函数式编程继续研究下去,我感觉它很酷!
实际上当年我是把它和设计模式并列为一种东西看待的。用得好,都能即艺术化自己的代码,又能得到实际的好处。
代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using FCSlib; using System.Data.SqlClient; using System.Data; namespace ConsoleApplication1 { static class helpmethod { public static Func<T1, Func<T2, TR>> curry<T1, T2, TR>(this Func<T1, T2, TR> func) { return p1 => p2 => func(p1, p2); } } class Program { #region 函数式编程实例:执行SQL代码 static void ExecuteSQL(SqlTransaction transaction, string sql) { var command = transaction.Connection.CreateCommand(); command.Transaction = transaction; command.CommandText = sql; command.ExecuteNonQuery(); } static void filldata1() { //下面的代码是需要重构的原始代码 using (var conn = new SqlConnection("server=(local);integrated security=SSPI;database=sanyopg")) { conn.Open(); try { using (var trans = conn.BeginTransaction()) { ExecuteSQL(trans, "create table people(id int,name ntext)"); trans.Commit(); } using (var trans = conn.BeginTransaction()) { ExecuteSQL(trans, "insert into people(id,name) values(1,'harry')"); ExecuteSQL(trans, "insert into people(id,name) values(2,'jane')"); ExecuteSQL(trans, "insert into people(id,name) values(3,'willy')"); ExecuteSQL(trans, "insert into people(id,name) values(4,'susan')"); ExecuteSQL(trans, "insert into people(id,name) values(5,'bill')"); ExecuteSQL(trans, "insert into people(id,name) values(6,'jennifer')"); ExecuteSQL(trans, "insert into people(id,name) values(7,'john')"); ExecuteSQL(trans, "insert into people(id,name) values(8,'anna')"); ExecuteSQL(trans, "insert into people(id,name) values(9,'bob')"); ExecuteSQL(trans, "insert into people(id,name) values(10,'mary')"); trans.Commit(); } } finally { conn.Close(); } } } static void filldata2() { //下面使用Lambda表达式重构 //变化:模块化发生在函数级,实现重用,但是修改没有扩展到外层的作用域,外层没有受到这个修改的“污染” using (var conn = new SqlConnection("server=(local);integrated security=SSPI;database=sanyopg")) { conn.Open(); try { using (var trans = conn.BeginTransaction()) { ExecuteSQL(trans, "create table people(id int,name ntext)"); trans.Commit(); } using (var trans = conn.BeginTransaction()) { Action<SqlTransaction,int,string> exec=(t,id,name)=> ExecuteSQL(t,string.Format("insert into people(id,name) values({0},'{1}')",id,name)); exec(trans, 1, "harry"); exec(trans, 2, "jane"); exec(trans, 3, "willy"); exec(trans, 4, "susan"); exec(trans, 5, "bill"); exec(trans, 6, "jennifer"); exec(trans, 7, "john"); exec(trans, 8, "anna"); exec(trans, 9, "bob"); exec(trans, 10, "mary"); trans.Commit(); } } finally { conn.Close(); } } } static void filldata3() { //使用部分应用和预计算重构 //filldata2()的实现,在每一行都接受trans参数。因此可以想到用部分应用和预计算进行重构 using (var conn = new SqlConnection("server=(local);integrated security=SSPI;database=sanyopg")) { conn.Open(); try { using (var trans = conn.BeginTransaction()) { ExecuteSQL(trans, "create table people(id int,name ntext)"); trans.Commit(); } using (var trans = conn.BeginTransaction()) { var sql="insert into people(IDataAdapter,namespace) values({0},'{1}')"; var exec =( (Func<SqlTransaction, Func<int, Action<string>>>) (t => id => name => ExecuteSQL(t, string.Format(sql, id, name))) )(trans); exec(1)("harry"); exec(2)("jane"); exec(3)("willy"); exec(4)("susan"); exec(5)("bill"); exec(6)("jennifer"); exec(7)("john"); exec(8)("anna"); exec(9)("bob"); exec(10)("mary"); trans.Commit(); } } finally { conn.Close(); } } } #endregion #region 预计算 static bool IsInListDump<T>(IEnumerable<T> list, T item) { var hashSet = new HashSet<T>(list); return hashSet.Contains(item); } static void preCal() { //每次都要调用一次IsInListDump函数创建哈希表,效率低下 string[] str = { "one","two","three","four","five","six","seven","eight","nine","ten" }; Console.WriteLine(IsInListDump(str,"three")); Console.WriteLine(IsInListDump(str,"no")); } static void preCal1() { //部分应用版本,但仍然效率不好 string[] str = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; var res1 = helpmethod.curry<IEnumerable<string>, string, bool>(IsInListDump<string>); var p1 = res1(str); Console.WriteLine(p1("three")); Console.WriteLine(p1("not")); } static Func<T, bool> listLookUp<T>(IEnumerable<T> list) { //hashet创建后就保存在了闭包里 var hashet = new HashSet<T>(list); return item => hashet.Contains(item); } static void preCal2() { //实现了预计算版本 string[] str = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; var lookup = listLookUp(str); Console.Write(lookup("three")); Console.Write(lookup("no")); } #endregion #region 缓存 #endregion #region 闭包 static void Closures() { Console.WriteLine(GetClosureFunction()(30)); } static Func<int, int> GetClosureFunction() { int val = 10; Func<int, int> internalAdd = x => x + val; Console.WriteLine(internalAdd(10)); val = 30; Console.WriteLine(internalAdd(10)); return internalAdd; } #endregion #region 部分应用 static void partApplytest1() { Func<int, int, int> add = delegate(int x, int y) { return x + y; }; Console.WriteLine(add(1, 2)); Func<int, Func<int, int>> add2 = delegate(int x) { return delegate(int y) { return x + y; }; }; Console.WriteLine(add2(1)(2)); var r1 = add2(1); var r2 = r1(2); Console.WriteLine(r2); Func<int, int, int> add3 = (x, y) => x + y; Console.WriteLine(add3(1, 4)); Func<int, Func<int, int>> add4 = x => y => x + y; Console.WriteLine(add4(1)(4)); Func<int, int, int> add5 = (x, y) => x + y; var f1 = Functional.Curry(add5); Console.WriteLine(f1(1)(4)); var f2 = Functional.Curry<int, int, int>((x, y) => x + y); Console.WriteLine(f2(1)(5)); Func<int, int, int> mult = (x, y) => x * y; var f3 = mult.Curry(); Console.WriteLine(f3(2)(5)); Console.WriteLine(calfun.Add(2, 3)); Console.WriteLine(calfun.AddC(2)(4)); Console.WriteLine(calfun.MultC(2)(3)); } #endregion #region 严格求值,非严格求值,惰性求值 //C#语言小部分采用了非严格求值策略,大部分还是严格求值策略 static int DoOneThing() { Console.WriteLine("DoOneThing 已经求值"); return 45; } static void DoSomeThing(bool a, int b) { Console.WriteLine("DoSomeThing begin"); if (a) Console.WriteLine("the b is {0} ", b); Console.WriteLine("DosomeThing ok"); } static void 非严格求值测试() { //由于DoOneThing()不参加计算,因此这是非严格求值的策略 bool b = true || DoOneThing() > 0; Console.ReadKey(); } static void 严格求值测试() { //由于传入的是false,所以DoOneThing()函数的值并没有被用到 //如果DoOneThing()是不个超级耗时的操作,那么就会白白浪费掉这段时间 //但这正是严格求值策略,严格求值策略的话整个表达式都会计算 DoSomeThing(false, DoOneThing()); Console.ReadKey(); } static void DoSomeThing1(Func<bool> a, Func<int> b) { Console.WriteLine("DoSomeThing begin"); if(a()) Console.WriteLine("the b is {0} ", b()*b()); Console.WriteLine("DosomeThing ok"); } static void 惰性求值1() { //DoSomeThing1的两个参数都是函数,这样就可以做到在需要的时候才执行 DoSomeThing1( ()=> true,()=>DoOneThing()); Console.ReadKey(); } static void DoSomeThing2(LazyS<bool> a, LazyS<int> b) { Console.WriteLine("DoSomeThing begin"); if (a.Value) Console.WriteLine("the b is {0} ", b.Value * b.Value); Console.WriteLine("DosomeThing ok"); } static void 惰性求值2() { DoSomeThing2(new LazyS<bool>(() => true), new LazyS<int>(() => DoOneThing())); Console.ReadKey(); } //.net中已经为我们包含了Lazy<T>类,因此可以不使用我们自定义的LazyS类 //系统自带的Lazy功能更多,并且支持多线程 static void DoSomeThing3(System.Lazy<bool> a, System.Lazy<int> b) { Console.WriteLine("DoSomeThing begin"); if (a.Value) Console.WriteLine("the b is {0} ", b.Value * b.Value); Console.WriteLine("DosomeThing ok"); } static void 惰性求值3() { DoSomeThing3(new System.Lazy<bool>(() => true), new System.Lazy<int>(() => DoOneThing())); Console.ReadKey(); } #endregion #region 预计算,缓存 static int DoSome1(int a, int b) { Console.WriteLine("开始计算C的值"); int c = a * a; Console.WriteLine("结束计算C的值"); return c + b; } static void 不使用预计算() { Console.WriteLine(string.Format("C1={0},C2={1}", DoSome1(10, 2), DoSome1(10, 4))); Console.ReadKey(); } static Func<int,int> DoSome2(int a) { Console.WriteLine("开始计算C的值"); int c = a * a; Console.WriteLine("结束计算C的值"); return s => c+s; } static void 预计算() { var fun1 = DoSome2(10); Console.WriteLine(string.Format("C1={0},C2={1}", fun1(2), fun1(4))); Console.ReadKey(); } #endregion //测试时需要自己加注释的方式一段段测试。 static void Main(string[] args) { 不使用预计算(); 预计算(); return; 惰性求值1(); 惰性求值2(); 惰性求值3(); return; 非严格求值测试(); Console.WriteLine("------------"); 严格求值测试(); return; //闭包 //Closures(); //Console.ReadLine(); //return; //partApplytest1(); //预计算 //preCal(); //preCal1(); preCal2(); Console.ReadLine(); return; //filldata2(); Console.ReadLine(); return; Func<SqlConnection, Func<string, DataSet>> exec1 = x => y => { using (x) { x.Open(); var com = x.CreateCommand(); var ds = new DataSet(); com.CommandText = y; var adapter = new SqlDataAdapter(com); adapter.Fill(ds); return ds; } }; var lib = exec1(new SqlConnection("server=(local);integrated security=SSPI;database=sanyopg")); var res1 = lib("select username from admin"); foreach (DataRow m in res1.Tables[0].Rows) { Console.WriteLine(m[0].ToString()); } /* var res2 = lib("select * from admin"); foreach (DataRow m in res2.Tables[0].Rows) { Console.WriteLine(m[1].ToString()); }*/ Console.ReadLine(); } public static class calfun { public static int Add(int x, int y) { return x + y; } public static readonly Func<int, Func<int, int>> AddC = Functional.Curry<int, int, int>(Add); public static int Mult(int x, int y) { return x * y; } public static readonly Func<int, Func<int, int>> MultC = Functional.Curry<int, int, int>(Mult); } public class LazyS<T> { T value; Func<T> function; bool isCreate; public LazyS(Func<T> func) { function = func; } public T Value { get { if (!isCreate) { value = function(); isCreate = true; } return value; } } } } }
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!

