C#函数式编程之部分应用

何谓函数式编程

相信大家在实际的开发中,很多情况下完成一个功能都需要借助多个类,那么我们这里的基本单元就是类。而函数式编程则更加细化,致使我们解决一个功能的基本单元是函数,而不是类,每个功能都是由多个函数构成,并且函数之间没有直接的关系。如果简单的文字描述还不足以让你理解,下面我们就配以图来演示。

如下图所示,图左是我们设计好的三个函数,而右边则是我们需要实现的功能。而我们需要做的就是利用这三个函数去完成对应的三个功能,笔者在这里只是进行简单而又形象的表述,实际的开发过程可能需要更多的函数,并且需要使用不同的函数式编程的方式组合才能完成对应的功能。

后面我们假设F1和F2进行组合可以完成功能G1,那么结果就如下图所示:

对应的其他功能我们依然是按照上面的方式进行组合就可以完成对应的功能,这样做必然有其对应的优点,对笔者而言最大的优点就是函数不受外部环境的影响,这里我们不能与类中的方法相提并论,因为方法会受到类上下文变量的影响,特别是在多线程的情况下会出现共享读和写的问题,而函数则不会,因为他只是通过参数的方式接收外部的变量,还有一点就是复用性很强,如果前期设计的充分,在后期开发过程中函数可以发挥到最大的作用。说了这么多废话,下面我们就可以开始我们的函数式编程的第一部分——部分应用。

部分应用

各位不用被这个名词吓坏,他主要是将我们多个参数的函数进行拆分,拆成多个只有一个参数的函数,比如下面这个函数,我们正常写的话都是这样写的:

1 Func<int, int, int> Add = (x, y) => x + y;

怎么调用相信笔者就不需要过多介绍了,下面我们就要让他能够支持部分应用:

1 Func<int, Func<int, int>> Add = x => y => x + y;

这下就应该明白了吧,只是在接收了一个值之后返回了下一个函数,然后我们再调用这个返回的函数就完成整个调用,我们是不是部分使用了这个函数?所以叫部分应用。下面我们来看看怎么使用这个函数:

1 var Add2 = Add(2);
2 var result = Add2(4);

这样分成两行比较容易看懂,但是我们可以仅仅使用一行就可以了,比如下面这个方式:

1 var result = Add(2)(5);

哇,是不是瞬间感觉高大上了,如果我们这个方法的参数再多点,就是括号加括号,相信别人看到你这行代码后就会呵呵了,然后心里一万个“某某”马奔腾。

我去,看到这的人会可能会吹嘘这又没有什么太特别的东西,就是函数返回函数。对就是函数返回函数,但是实际运用起来你就会发现舒畅多了,下面笔者简单的举一个比较靠谱的例子来说明部分应用能够带给我们什么,比如我们经常需要执行SQL语句,当然需要使用SqlConnection,然后附加上对应的SQL语句,为此我们可以开发一个简单的函数,用来简化这一过程:

 1             Func<SqlConnection, Func<String, DataSet>> ExecSql = x => y =>
 2                     {
 3                         using (x)
 4                         {
 5                             x.Open();
 6                             var com = x.CreateCommand();
 7                             DataSet ds = new DataSet();
 8                             com.CommandText = y;
 9                             SqlDataAdapter adapter = new SqlDataAdapter(com);
10                             adapter.Fill(ds);
11                             return ds;
12                         }
13                     };

然后调用起来就简单多了,我们只要传递给对应的SqlConnection对象,然后对应的返回值我们就可以用来执行我们的SQL语句了,具体的使用示例如下所示:

1 var esql = ExecSql(new SqlConnection("xxx"));
2
3 var rds = esql("select xxxx from xxx");
4 rds = esql("select ffff from ffff");

但是做到这还没有结束,面对那些总是想出奇怪问题的人,我们还有一个需要做,就是我们可能先要传递SQL语句,然后再传递对应的SqlConnection对象,没问题,我们专门为此写个函数:

1 Func<String, Func<SqlConnection, DataSet>> ExecSqlT = x => y => ExecSql(y)(x);

我们就继续该怎么调用就调用吧,但是上面都是从一开始就利用部分应用的方式来写,实际情况可能是已经写好的普通的方式,需要转换成部分应用的方式。那么下面我们可以自己先手动的写几个扩展,以便于以后的使用,首先我们来写存在两个参数和返回值的扩展:

1     public static class Functional
2     {
3         public static Func<T1, Func<T2, T3>> Currey<T1, T2, T3>(this Func<T1, T2, T3> func)
4         {
5             return x => y => func(x, y);
6         }
7 }

有了这个扩展之后我们再把上面的例子改写:

 1             var ExecSql = Functional.Currey<SqlConnection, String, DataSet>((x, y) =>
 2                     {
 3                         using (x)
 4                         {
 5                             x.Open();
 6                             var com = x.CreateCommand();
 7                             DataSet ds = new DataSet();
 8                             com.CommandText = y;
 9                             SqlDataAdapter adapter = new SqlDataAdapter(com);
10                             adapter.Fill(ds);
11                             return ds;
12                         }
13                     });

这样我们就可以按照我们正常的形式来写,然后调用FunctionalCurrey就可以了,当然这里需要显示的传递泛型参数,有些情况下则不需要。

如果需要扩展更多参数的可以对应的写下去就可以了。当然上面仅仅只是针对没有参数的情况,我们也可以对Action也进行扩展:

1 public static Func<T1, Action<T2>> Currey<T1, T2>(this Action<T1, T2> func)
2 {
3       return x => y => func(x, y);
4 }

到此我们就解决了将普通函数转换成部分应用方式的函数,但是问题就来了。如果我们一开始写的是部分应用方式的函数,怎么将其转换成普通的函数呢?自然我们还需要下面的扩展能够将其转换回去:

1 public static Func<T1, T2, T3> UnCurrey<T1, T2, T3>(this Func<T1, Func<T2, T3>> func)
2 {
3      return (x, y) => func(x)(y);
4 }
时间: 2024-10-06 02:00:34

C#函数式编程之部分应用的相关文章

Python学习笔记八:文件操作(续),文件编码与解码,函数,递归,函数式编程介绍,高阶函数

文件操作(续) 获得文件句柄位置,f.tell(),从0开始,按字符数计数 f.read(5),读取5个字符 返回文件句柄到某位置,f.seek(0) 文件在编辑过程中改变编码,f.detech() 获取文件编码,f.encoding() 获取文件在内存中的编号,f.fileno() 获取文件终端类型(tty.打印机等),f.isatty() 获取文件名,f.name() 判断文件句柄是否可移动(tty等不可移动),f.seekable() 判断文件是否可读,f.readable() 判断文件是

PYTHON修饰器的函数式编程

转自:http://coolshell.cn/articles/11265.html Python修饰器的函数式编程 Python的修饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然好像,他们要干的事都很相似--都是想要对一个已有的模块做一些"修饰工作",所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能

函数式编程

函数式编程 函数式编程的三大特性: immutable data 不可变数据 first class functions 尾递归优化 函数式编程的准则:不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你. 如何变得functional: 1. 没有共享变量 2.通过参数和返回值传递数据 3. 函数里没有临时变量 对现有的代码进行重构(refactoring) 使得代码具有functional programming的优点 lambda a, x: x(a) lambda函数可以

Python学习:映射函数(map)和函数式编程工具(filter和reduce)

在序列中映射函数map map函数会对一个序列对象中的每一个元素应用被传入的函数,并且返回一个包含了所有函数调用结果的一个列表. 例1: def sum(x):     return x + 10 L1 = [1,2,3,4,5,6,7] L = map(sum, L1) #结果为[11, 12, 13, 14, 15, 16, 17] map还有更高级的使用方法,例如提供了序列作为参数,它能够并行返回分别以每个序列中的元素作为函数对应参数得到的结果的列表.如例2所示. 例2: def sum(

javaScript函数式编程-包含闭包、链式优化及柯里化

本文着重介绍个人理解的函数式编程. 函数式编程个人理解为:以函数为主要载体的编程方式. 好处: 语义更加清晰 可复用性高 可维护性好 作用域局限.副作用少 基本函数式编程: //实现数组中每个单词首字母大写 //一般写法 const arr = ['apple','orange','pear']; for(const i in arr) { const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); //slice()从已有的

javascript - Underscore 与 函数式编程

<Javascript函数式编程 PDF> # csdn下载地址http://download.csdn.net/detail/tssxm/9713727 Underscore # githubhttps://github.com/jashkenas/underscore # 中文官方网站http://www.css88.com/doc/underscore/ # CDN<script src="https://cdn.bootcss.com/underscore.js/1.8

LUA 函数式编程demo

什么是函数式编程 http://www.zhihu.com/topic/19585411/hot 函数式编程的本质函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射.也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态.比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的.在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合.

[Java] 函数式编程相关概念 - 笔记1

Java 8 引入了 lambda 表达式,以及函数式编程风格.在了解函数式编程过程中,做了些笔记,摘录于本文. 嵌套函数( Nested Function ) 1. 嵌套函数,是指在另一个函数里面定义的一个函数.外层的函数,这里简称为外层函数. 2. 函数的嵌套可以是多层嵌套.嵌套函数可以看到其全部的外层函数的非局部变量.在实际程序中,嵌套的层数一般很少.下面是一个三层嵌套的例子, innerOfInner 也可以访问在 outer 函数体重定义的变量 x . function outer()

python_way.day7 模块(configparser,xml,shutil,subprocess)、面向对象(上)(创建类,类的构成,函数式编程与面向对象编程的选择,类的继承)

python_way.day7 1.模块 configparser,xml,shutil,subprocess 2.面向对象(上) 创建类,类的构成,函数式编程与面向对象编程的选择,类的继承 1.模块 configparser 用于处理特定格式的文件,其本职上使用open来操作,只能是  [test1] 特定的格式 [test1] k1 = 123 k2 = True [test2] k1 = 123 k2 = v1 文件内容 1.获取 import configparser #打开文件找到文件

为什么函数式编程在Java中很危险?

摘要:函数式编程这个不温不火的语言由来已久.有人说,这一年它会很火,尽管它很难,这也正是你需要学习的理由.那么,为什么函数式编程在Java中很危险呢?也许这个疑问普遍存在于很多程序员的脑中,作者Elliotte对此发表了一些见解,我们一起来看看他是怎么说的. 在我的日常工作中,我身边的开发者大多是毕业于CS编程顶级院校比如MIT.CMU以及Chicago,他们初次涉及的语言是Haskell.Scheme及Lisp.他们认为函数式编程是一种自然的.直观的.美丽的且高效的编程样式.但奇怪的是,我和我