C#函数式编程中的部分应用详解

何谓函数式编程

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

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

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

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

部分应用

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

代码如下:

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

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

代码如下:

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

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

代码如下:

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

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

代码如下:

var result = Add(2)(5);

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

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

代码如下:

Func<SqlConnection, Func<String, DataSet>> ExecSql = x => y =>
                    {
                        using (x)
                        {
                            x.Open();
                            var com = x.CreateCommand();
                            DataSet ds = new DataSet();
                            com.CommandText = y;
                            SqlDataAdapter adapter = new SqlDataAdapter(com);
                            adapter.Fill(ds);
                            return ds;
                        }
                    };

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

代码如下:

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

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

代码如下:

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

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

代码如下:

public static class Functional
    {
        public static Func<T1, Func<T2, T3>> Currey<T1, T2, T3>(this Func<T1, T2, T3> func)
        {
            return x => y => func(x, y);
        }
}

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

代码如下:

var ExecSql = Functional.Currey<SqlConnection, String, DataSet>((x, y) =>
                    {
                        using (x)
                        {
                            x.Open();
                            var com = x.CreateCommand();
                            DataSet ds = new DataSet();
                            com.CommandText = y;
                            SqlDataAdapter adapter = new SqlDataAdapter(com);
                            adapter.Fill(ds);
                            return ds;
                        }
                    });

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

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

代码如下:

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

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

代码如下:

public static Func<T1, T2, T3> UnCurrey<T1, T2, T3>(this Func<T1, Func<T2, T3>> func)
{
    return (x, y) => func(x)(y);
}

(0)

相关推荐

  • C#函数式编程中的惰性求值详解

    惰性求值 在开始介绍今天要讲的知识之前,我们想要理解严格求值策略和非严格求值策略之间的区别,这样我们才能够深有体会的明白为什么需要利用这个技术.首先需要说明的是C#语言小部分采用了非严格求值策略,大部分还是严格求值策略.首先我们先演示非严格求值策略的情况,我们先在控制台项目中写一个DoOneThing方法. 然后在Main方法中写入下面这串代码: 然后我们运行程序,会发现DoOneThing方法并没有执行.当然这看起来也很正常,因为这是或,并且第一个已经是true了.整个表达式就是true了,自

  • C#函数式编程中的标准高阶函数详解

    何为高阶函数 大家可能对这个名词并不熟悉,但是这个名词所表达的事物却是我们经常使用到的.只要我们的函数的参数能够接收函数,或者函数能够返回函数,当然动态生成的也包括在内.那么我们就将这类函数叫做高阶函数.但是今天我们的标题并不是高阶函数,而是标准高阶函数,既然加上了这个标准,就意味着在函数式编程中有一套标准的函数,便于我们每次调用.而今天我们将会介绍三个标准函数,分别为Map.Filter.Fold.  Map 这个函数的作用就是将列表中的每项从A类型转换到B类型,并形成一个新的类型.下面我们可

  • C#函数式编程中的部分应用详解

    何谓函数式编程 相信大家在实际的开发中,很多情况下完成一个功能都需要借助多个类,那么我们这里的基本单元就是类.而函数式编程则更加细化,致使我们解决一个功能的基本单元是函数,而不是类,每个功能都是由多个函数构成,并且函数之间没有直接的关系.如果简单的文字描述还不足以让你理解,下面我们就配以图来演示. 如下图所示,图左是我们设计好的三个函数,而右边则是我们需要实现的功能.而我们需要做的就是利用这三个函数去完成对应的三个功能,笔者在这里只是进行简单而又形象的表述,实际的开发过程可能需要更多的函数,并且

  • C#函数式编程中的缓存技术详解

    缓存技术 该节我们将分成两部分来讲解,第一部分为预计算,第二部分则为缓存.缓存这个技术对应从事开发的人员来说是非常熟悉的,从页面缓存到数据库缓存无处不在,而其最重要的特点就是在第一次查询后将数据缓存,在以后的查询过程中就无需重新计算而直接从内存中将结果返回,大大提高了性能,而我们这里的缓存则集中运用在函数上.  预计算 可能一些人并不能立马理解这个词的含义,所以我们就简单的从生活例子出发介绍一下.很多人在工作中一定会这样做事,比如上级吩咐了你一件事,但是这件事的后半部分要等另一个同事做好之后把对

  • Python函数式编程之返回函数实例详解

    目录 看代码: 用filter函数来计算素数 用Python高阶函数来实现这个算法: 高阶函数实现打印小于100的素数: 总结 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回. 看代码: # -*- coding: utf-8 -*- # @File : 返回函数的高阶函数.py # @author: Flyme awei # @email : Flymeawei@163.com # @Time : 2022/8/21 14:48 def sum_fun(*args): def

  • Android编程中HTTP服务用法详解

    本文实例讲述了Android编程中HTTP服务用法.分享给大家供大家参考,具体如下: 在Android中,除了使用java.net包下的API访问HTTP服务之外,我们还可以换一种途径去完成工作.Android SDK附带了Apache的HttpClient API.Apache HttpClient是一个完善的HTTP客户端,它提供了对HTTP协议的全面支持,可以使用HTTP GET和POST进行访问.下面我们就结合实例,介绍一下HttpClient的使用方法. 我们新建一个http项目,项目

  • C++编程中的格式化输出详解

    在输出数据时,为简便起见,往往不指定输出的格式,由系统根据数据的类型采取默认的格式,但有时希望数据按指定的格式输出,如要求以十六进制或八进制形式输出一个 整数,对输出的小数只保留两位小数等.有两种方法可以达到此目的.一种是使用控制符的方法:第2种是使用流对象的有关成员函数.分别叙述如下. 使用控制符控制输出格式 控制格式的使用方法这里不再赘述,仅举例说明 [例] 用控制符控制输出格式. #include <iostream> #include <iomanip>//不要忘记包含此头

  • C/C++编程中const的使用详解

    目录 1 概述:const和define的区别 2. 修饰局部变量 3. 常量指针与指针常量 4. 修饰函数的参数 5. 修饰函数的返回值 6. 修饰全局变量 总结 1 概述:const和define的区别 先看一个典型的程序: #include<iostream> using namespace std; int main() { int num = 1; #define t1 num + num #define t2 t1 % t1 cout << "t2 is &q

  • 理解javascript函数式编程中的闭包(closure)

    闭包(closure)是函数式编程中的概念,出现于 20 世纪 60 年代,最早实现闭包的语言是 Scheme,它是 LISP 的一种方言.之后闭包特性被其他语言广泛吸纳. 闭包的严格定义是"由函数(环境)及其封闭的自由变量组成的集合体."这个定义对于大家来说有些晦涩难懂,所以让我们先通过例子和不那么严格的解释来说明什么是闭包,然后再举例说明一些闭包的经典用途. 什么是闭包 通俗地讲, JavaScript 中每个的函数都是一个闭包,但通常意义上嵌套的函数更能够体 现出闭包的特性,请看

  • Python中itertools的用法详解

    iterator 循环器(iterator)是对象的容器,包含有多个对象.通过调用循环器的next()方法 (next()方法,在Python 3.x中),循环器将依次返回一个对象.直到所有的对象遍历穷尽,循环器将举出StopIteration错误. 在for i in iterator结构中,循环器每次返回的对象将赋予给i,直到循环结束.使用iter()内置函数,我们可以将诸如表.字典等容器变为循环器.比如 for i in iter([2, 4, 5, 6]): print i 标准库中的i

  • Kotlin中常见的符号详解

    前几年的Google I/O大会上,Google正式宣布,Kotlin将会成为Android开发的官方支持语言.除了Android外,Kotlin还可以完全作为服务端开发的语言,比如在未来的Spring 5就将对Kotlin提供强大的支持.以及浏览器编程语言,与JS进行交互. Kotlin是一门静态语言,支持多种平台,包括移动端.服务端以及浏览器端,此外,Kotlin还是一门融合了面向对象与函数式编程的语言,支持泛型.安全的空判断,并且Kotlin与Java可以做到完全的交互. 现在介绍Kotl

随机推荐