Boost.Lambda是什么?
Boost Lambda庫(kù)是C++模板庫(kù),以C++語(yǔ)言實(shí)現(xiàn)了lambda抽象.Lambda這個(gè)術(shù)語(yǔ)來(lái)自函數(shù)編程語(yǔ)言和lambda閉包理論,lambda抽象實(shí)際上定義了匿名函數(shù).了解過(guò)C#新引入的匿數(shù)函數(shù)特性或Lisp編程的人,對(duì)這些概念理解會(huì)有很大幫助.Lambda庫(kù)設(shè)計(jì)的主要?jiǎng)訖C(jī)是為STL算法提供靈活方便的定義匿名函數(shù)對(duì)象的機(jī)制.這個(gè)Lambda庫(kù)究竟是有什么用呢?代碼勝千言!看下面將STL容器中的元素打印到標(biāo)準(zhǔn)輸出上的代碼.
for_each(a.begin(), a.end(), std::cout << _1 << ' ');
表達(dá)式std::cout << _1 << ' '定義了一元函數(shù)對(duì)象.變量_1是函數(shù)的形參,是實(shí)參的占位符.每次for_each的迭代中,函數(shù)帶著實(shí)際的參數(shù)被調(diào)用,實(shí)際參數(shù)取代了占位符,然后函數(shù)體里的內(nèi)容被執(zhí)行.Lambda庫(kù)的核心就是讓你能像上面所展示的那樣,在STL算法的調(diào)用點(diǎn),定義小的匿名函數(shù)對(duì)象.
Lambda庫(kù)的安裝
Lambda庫(kù)只由頭文件組成,這就意味著你不需要進(jìn)行任何編譯,連接,生成二進(jìn)制庫(kù)的動(dòng)作,只需要boost庫(kù)頭文件路徑包含進(jìn)你的工程中即可使用.
與現(xiàn)代的C++語(yǔ)言一樣,在使用時(shí)你需要聲明用到的名字空間,把下列的代碼包含在你的源文件頭:
using namespace boost::lambda;
Boost Lambda庫(kù)的動(dòng)機(jī)
在標(biāo)準(zhǔn)模板庫(kù)STL成為標(biāo)準(zhǔn)C++的一部分后,典型的STL算法對(duì)容器中元素的操作大都是通過(guò)函數(shù)對(duì)象(function objects)完成的.這些函數(shù)作為實(shí)參傳入STL算法.
任何C++中以函數(shù)調(diào)用語(yǔ)法被調(diào)用的對(duì)象都是函數(shù)對(duì)象.STL對(duì)某些常見(jiàn)情況預(yù)置了些函數(shù)對(duì)象.比如:
plus,less,not1下面就是標(biāo)準(zhǔn)
plus模板的一種可能實(shí)現(xiàn):
template <class T>
struct plus : public binary_function <T, T, T> {
T operator()(const T& i, const T& j) const {
return i + j;
}
};
基類binary_function<T, T, T>包含了參數(shù)和函數(shù)對(duì)象返回類型的類型定義,這樣可使得函數(shù)對(duì)象可配接.
除了上面提到的基本的函數(shù)對(duì)象外,STL還包含了
binder模板,將可配接的二元函數(shù)中的某個(gè)實(shí)參固定為常量值,來(lái)創(chuàng)建一個(gè)一元函數(shù)對(duì)象.比如:
class plus_1 {
int _i;
public:
plus_1(const int& i) : _i(i) {}
int operator()(const int& j) { return _i + j; }
};
上面的代碼顯性地創(chuàng)建了一個(gè)函數(shù)對(duì)象,將其參數(shù)加1.這樣的功能可用plus模板與binder模板(bind1st來(lái)等效地實(shí)現(xiàn).舉例來(lái)說(shuō),下面的兩行表達(dá)式創(chuàng)建了一個(gè)函數(shù)對(duì)象,當(dāng)它被調(diào)用時(shí),將返回1與調(diào)用參數(shù)的和.
plus_1(1)
bind1st(plus<int>(), 1)
plus<int>就是計(jì)算兩個(gè)數(shù)之和的函數(shù)對(duì)象.bind1st使被調(diào)用的函數(shù)對(duì)象的第一個(gè)參數(shù)綁定到常量1.作為上面函數(shù)對(duì)象的使用示例,下面的代碼就是將容器a中的元素加1后,輸出到標(biāo)準(zhǔn)輸出設(shè)備:
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
bind1st(plus<int>(), 1));
為了使binder更加通用,STL包含了適配器
(adaptors)用于函數(shù)引用與指針,以及成員函數(shù)的配接.
所有這些工具都有一個(gè)目標(biāo),就是為了能在STL算法的調(diào)用點(diǎn)有可能指定一個(gè)匿名的函數(shù),換句說(shuō),就是能夠使部分代碼片斷作為參數(shù)傳給調(diào)用算法函數(shù).但是,標(biāo)準(zhǔn)庫(kù)在這方面只做了部分工作.上面的例子說(shuō)明用標(biāo)準(zhǔn)庫(kù)工具進(jìn)行匿名函數(shù)的定義還是很麻煩的.復(fù)雜的函數(shù)調(diào)用表達(dá)式,適配器,函數(shù)組合符都使理解變得困難.另外,在運(yùn)用標(biāo)準(zhǔn)庫(kù)這些方法時(shí)還有明顯的限束.比如,標(biāo)準(zhǔn)C++98中的binder只允許二元函數(shù)的一個(gè)參數(shù)被綁定,而沒(méi)有對(duì)3參數(shù),4參數(shù)的綁定.這種情況在TR1實(shí)施后,引進(jìn)了通用的binder后可能改善,對(duì)于使用MSVC的程序員,有興趣還可以查看下微軟針對(duì)VS2008發(fā)布的TR1增強(qiáng)包.
但是不管怎樣,Lambda庫(kù)提供了針對(duì)這些問(wèn)題比較優(yōu)雅的解決方法:
對(duì)匿名函數(shù)以直觀的語(yǔ)義進(jìn)行創(chuàng)建,上面的例子可改寫成:
transform(a.begin(), a.end(), ostream_iterator<int>(cout),
1 + _1);
更直觀點(diǎn):
for_each(a.begin(), a.end(), cout << (1 + _1));
絕大部分對(duì)函數(shù)參數(shù)綁定的限制被去除,在實(shí)際C++代碼中可以綁定任意的參數(shù)
分離的函數(shù)組合操作不再需要了,函數(shù)組合被隱性地支持.
Lambda表達(dá)式介紹
Lambda表達(dá)在函數(shù)式編程語(yǔ)言中很常見(jiàn).在不同語(yǔ)言中,它們的語(yǔ)法有著很大不同,但是lambda表達(dá)式的基本形式是:
lambda x1...xn.e
lambda表達(dá)式定義了匿名函數(shù),并由下列的元素組成
- 函數(shù)的參數(shù):x1...xn
- 表達(dá)式e,以參數(shù)x1...xn的形式計(jì)算函數(shù)的值
一個(gè)簡(jiǎn)單的lambda表達(dá)式的例子是:
(lambda x y.x+y) 2 3 = 2 + 3 = 5
在lambda表達(dá)式的C++版本中,表達(dá)式中x1...xn不需要,已預(yù)定義形式化的參數(shù).在現(xiàn)在Boost.Lambda庫(kù)中,存在三個(gè)這樣的預(yù)定義的參數(shù),叫做占位符:_1,_2,和_3.它們分別指代在lambda表達(dá)式中的第一,二,三個(gè)參數(shù).比如,下面這樣的lambda表達(dá)式:
lambda x y.x+y
C++定義的形式將會(huì)是這樣:
_1 + _2
因此在C++中的lambda表達(dá)式?jīng)]有語(yǔ)義上所謂的關(guān)鍵字.占位符作為運(yùn)算符使用時(shí)就隱性地意味著運(yùn)算符調(diào)用是個(gè)lambda表達(dá)式.但是只有在作為運(yùn)算符調(diào)用才是這樣.當(dāng)Lambda表達(dá)式包含函數(shù)調(diào)用,控制結(jié)構(gòu),轉(zhuǎn)換時(shí)就需要特殊的語(yǔ)法調(diào)用了.更為重要的是,作為函數(shù)調(diào)用是需封裝成binder函數(shù)的形式.比如,下面這個(gè)lambda表達(dá)式:
lambda x y.foo(x,y)
不應(yīng)寫成foo(_1,_2),對(duì)應(yīng)的C++結(jié)構(gòu)應(yīng)如下:
bind(foo, _1, _2)
對(duì)于這種表達(dá)式,更傾向于作為綁定表達(dá)式bind expressions
lambda表達(dá)式定義了C++的函數(shù)對(duì)象,因此,對(duì)于函數(shù)調(diào)用的形式跟其他的函數(shù)對(duì)象一樣,比如:(_1 + _2)(i, j).
性能
性能,運(yùn)行效率,總是C++程序員關(guān)心的話題.理論上,相對(duì)于手寫循環(huán)代碼,使用STL算法和Lambda函數(shù)對(duì)象的所有運(yùn)行開(kāi)銷,可以通過(guò)編譯優(yōu)化消除掉.這種優(yōu)化取決于編譯器,實(shí)際中的編譯器大都能做到.測(cè)試表明,性能會(huì)有下降,但是影響不大,對(duì)于代碼的效率和簡(jiǎn)潔之間的權(quán)衡,只能由程序員自己做出判斷了.
Lambda庫(kù)的設(shè)計(jì)與實(shí)現(xiàn)中大量運(yùn)用了模板技術(shù),造成對(duì)于同一模板需要大量的遞歸實(shí)例化.這一因素可能使構(gòu)建復(fù)雜邏輯的lambda表達(dá)式,不是一個(gè)非常理想的做法.因?yàn)榫幾g這些表達(dá)式需要大量的內(nèi)存,從而使編譯時(shí)間變得非常慢,這在一些大型項(xiàng)目中會(huì)更加突出.還有在發(fā)生編誤錯(cuò)誤時(shí),引發(fā)的大量錯(cuò)誤信息,不能有效地指出真正錯(cuò)誤之處.最后點(diǎn),C++標(biāo)準(zhǔn)建議模板的嵌套層次不要超過(guò)17層來(lái)防止導(dǎo)致無(wú)限遞歸,而復(fù)雜的Lambda表達(dá)式模板會(huì)很容易超過(guò)這一限制.雖然大多數(shù)編譯器允許更深層次的模板嵌套,但是通常需要顯性地傳入一個(gè)命令行參數(shù)才能做到.
參考
大多數(shù)內(nèi)容是從Boost.Lambday庫(kù)在線文檔參考翻譯而成
posted on 2008-05-18 16:03
len 閱讀(8629)
評(píng)論(5) 編輯 收藏 引用 所屬分類:
程序開(kāi)發(fā)