前言
我们经常所编程语言的的进步速度是落后于硬件的发展速度的。
但是最近几年,闭包语法在各个语言中都有自己的体现形式,如:
• C语言中使用函数指针作为回调函数的入口;
• Java和C#语言中的Lambda语法表达式;
• Objective-C语言中的Blocks语法;
• C#语言中的Delegates语法;
• C++语言中的Functions对象;
正文
历史由来
计算机的目的就是计算,也可以这样狭隘的理解,计算机要能完成可计算公式的计算和逻辑。要完成这样的事情,需要一套东西去支持。
阿隆佐邱奇(Alonzo Church)发明了Lambda演算,也就是λ演算。
Lambda演算
λ演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义、函数如何被应用以及递归的形式系统。
因为这里Lambda演算不是讨论的重点,我举几个简单的例子,让大家明白就好。
在Lambda演算中,每个表达式都代表一个函数,这个函数有一个参数,并且会返回一个值。也就是说在Lambda演算中只有函数。
- Lambda演算的基本定义:
λx. E,x是参数,并且有且仅有一个参数,E是函数体。 - 函数的应用:
E1 E2,E1是个函数,E2也是个函数,并且每个函数都有返回值,E2的返回值当成λx. E的x,带人到E1的函数中,在返回E1的结果。
这样还是很难理解,再来个个例子。
现在我们定义一个数学函数f(x) = x + 2,数学意义很明显,就给x加上2。怎么用Lambda演算弄?
- Lambda演算的基本定义:
λx. x + 2,x是参数,x + 2函数体。 - 函数的应用:
当有个参数λa.a 3的时候就是这个样子(λx. x + 2) (λa.a 3),可以写成也就符合上面的E1 E2格式。结果就是(λx. x + 2)3 = 3 + 2 = 5
这里有没有特别的熟悉,说说编程函数定义。
1. returnValue funcitonName(parameter){ 2. methodBody 3. }
假如这个函数只能传入一个参数,那么是不是就是 λx. E的简单表达,原来函数的由来可以这样追溯。
使用方法
C++语言中的Functions对象:
#include <iostream> #include <string> #include <vector> using namespace std; //构造string向量 vector<string> GetNameList() { static vector<string> names; names.push_back("刘辉"); names.push_back("李静波"); names.push_back("崔亚允"); names.push_back("赵雅"); names.push_back("管辉"); names.push_back("白志刚"); names.push_back("王斌"); names.push_back("白雅静"); names.push_back("张浩"); return names; } void foo(string myname) { vector<string> names = GetNameList(); //通过遍历string向量,使用闭包完成判断条件 vector<string>::iterator i = find_if(names.begin(), names.end(), [&](const string& s){ //判断操作值与参数相等是否相等 return s == myname; }); //输出判断得到的结果 cout <<(string)(*i) << endl; } int main(int argc, const char * argv[]) { foo("刘辉"); return 0; }
1、声明闭包变量
C++中闭包的声明语法,要使用 function 类型来声明变量,如下所示:
std::function<float(float)> colsure;
其中,第一个 float 代表了闭包的返回值类型,第二个 float 代表了闭包的参数数据类型为浮点型
2、赋值闭包变量
C++中闭包的赋值语法,要使用 [=] 或 [&] 符号开头,如下所示:
colsure = [=](float f) { f += 10.0f; return f; };
其中, [=] 代表了我们将要进行的闭包传值是单向赋值
[&] 在使用过程中,经常作为引用传值使用,如下所示:
std:function<float(float&)> col; col = [&](float& f){ f += 10.0f; return f; };
3、使用闭包变量
使用 [=] 和 [&] 声明并且赋值后的闭包变量,在使用结束后的结果如下:
//声明一个浮点型变量 float floatValue = 1.0f; //声明计算结果浮点型变量 float resultValue = 0.0f; //使用[=]类型的闭包变量 resultValue = colsure(floatValue); //输入结果 std::cout<<floatValue<<":"<<resultValue<<endl; //使用[&]类型的闭包变量 resultValue = col(floatValue); std::cout<<floatValue<<":"<<resultValue<<endl;
[=] 和 [&] 分别代表了值传递和引用传递的两种方式。
C#语言中的Lambda表达式:
Lambda表达式实在C#4中出现的语法糖,用来提高程序的开发效率,简化Func类型变量和delegate对象的写法。
常见的代码如下:
using System; using System.Collections.Generic; namespace LambdaSample { class MainClass { public static List<string> GetNameList() { List<string> names = new List<string> (); names.Add ("刘辉"); names.Add ("崔亚允"); names.Add ("李静波"); names.Add ("赵雅"); names.Add ("管辉"); names.Add ("白志刚"); return names; } public static void foo(string myname) { //获取姓名列表 List<string> names = GetNameList (); //通过Lambda表达式作为判断条件进行查询 string result = names.Find ((x) => { return x == myname; }); //输入查询结果 Console.WriteLine(result); } public static void Main (string[] args) { //测试 foo ("崔亚允"); } } }
1、声明C#语言中的Lambda表达式变量
在C#语言中,可以通过 Func 类型来声明Lambda变量,如下所示:
Func<float,float> func;
或者使用 delegate 类型来声明Lambda变量,如下所示:
//定义一个delegate类型Interface delegate float Interface(float x); public static void Main (string[] args) { //使用Interface类型来声明Lambda变量 Inteface inter_func; }
2、赋值C#语言中的Lambda表达式变量
在C#语言中,使用 => goes to 运算符来生成变量,如下所示:
func = (x) => { x += 10.0f; return x; };
=> goes to运算符的左边是闭包类型的浮点型参数,右边是闭包变量准备进行的逻辑运算,以 {} 包括。
3、使用C#语言中的Lambda表达式变量
和调用函数一样,如下所示:
//声明Func类型变量 Func<float,float> func; //为func赋值Lambda闭包 func = (x) => { x += 10.0f; return x; }; float floatValue = 1.0f; float resultValue = 0.0f; //调用func变量,计算结果并且赋值给resultValue resultValue = func (floatValue); //进行输出 Console.WriteLine (resultValue);
总结
其实在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
注意咯:
闭包一词经常和匿名函数混淆。这可能是因为两者经常同时使用,但是它们是不同的概念!