標籤:
前言
我們經常所程式設計語言的的進步速度是落後於硬體的發展速度的。
但是最近幾年,閉包文法在各個語言中都有自己的體現形式,例如
• C語言中使用函數指標作為回呼函數的入口;
• Java和C#語言中的Lambda文法運算式;
• Objective-C語言中的Blocks文法;
• C#語言中的Delegates文法;
• C++語言中的Functions對象;
曆史
Peter J. Landin 在1964年將術語 閉包 定義為一種包含 環境成分 和 控製成分 的實體,用於在他的SECD 機器上對錶達式求值。Joel Moses 認為是 Landin 發明了 閉包 這一術語,用來指代某些其開放綁定(自由變數)已經由其文法環境完成閉合(或者綁定)的 lambda 運算式,從而形成了 閉合的運算式,或稱閉包。
使用方法
C++語言中的Functions對象:
C++11標準開始支援閉包,這是一種特殊的函數對象,由特殊的語言結構—— lambda運算式 自動構建。
C++閉包中儲存了全部nonlocal變數的拷貝或引用。
如果是對外界環境中的對象的引用,且閉包執行時該外界環境的變數已經不存在(如在調用棧上已經unwinding),那麼可導致undefined behavior,因為C++並不擴充這些被引用的外界環境的變數的生命期。
常見代碼如下:
//// main.cpp// Interface//// Created by lewis on 4/30/15.// Copyright (c) 2015 lewis. All rights reserved.//#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;
通過列印的結果,
1.0:10.010.0:10.0
[=] 和 [&] 分別代表了值傳遞和引用傳遞的兩種方式。
Objective-C中的Blocks變數:
Blocks文法支援的系統版本為OS X 8或者更高版本,iOS 4或者更高版本.
常見的代碼如下:
//// main.m// BlockSample//// Created by lewis on 4/30/15.// Copyright (c) 2015 lewis. All rights reserved.//#import <Foundation/Foundation.h>NSArray *GetNameList(){ static NSMutableArray *names = nil; if (names == nil) { names = [[NSMutableArray alloc] init]; [names addObject:@"劉輝"]; [names addObject:@"崔亞允"]; [names addObject:@"李靜波"]; [names addObject:@"趙雅"]; [names addObject:@"管輝"]; [names addObject:@"王斌"]; [names addObject:@"張浩"]; } return names;}void foo(NSString *myname){ //擷取到姓名列表 NSArray *names = GetNameList(); //通過遍曆數組對象,使用Block進行判斷 NSInteger index = [names indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { return [obj isEqualToString:myname]; }]; //輸出結果 NSLog(@"%@",names[index]);}int main(int argc, const char * argv[]){ foo(@"崔亞允"); return 0;}
1、聲明Block文法變數
float(^block)(float);
我們會發現,Blocks文法變數的聲明文法與函數指標變數的聲明文法 float(*pointer)(float); 非常類似,只是在運算子號上有區別;
2、賦值Block文法變數
block = ^ (float f){ f += 10.0f; return f;};
在對Blocks變數進行賦值時,要注意所有的Block變數都是用 "^" 運算子號來開頭,並且有傳回值類型的Block變數,在Block代碼塊內部的return 傳回型別要相同。
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類型Interfacedelegate 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變數,計算結果並且賦值給resultValueresultValue = func (floatValue);//進行輸出Console.WriteLine (resultValue);
總結
其實在電腦科學中,閉包(Closure)是詞法閉包(Lexical Closure)的簡稱,是引用了自由變數的函數。這個被引用的自由變數將和這個函數一同存在,即使已經離開了創造它的環境也不例外。所以,有另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體。閉包在運行時可以有多個執行個體,不同的引用環境和相同的函數組合可以產生不同的執行個體。
需要注意的一點是
閉包一詞經常和匿名函數混淆。這可能是因為兩者經常同時使用,但是它們是不同的概念!!!!
[iOS、Unity、Android] 淺談閉包的使用方法