.NET Lambda表達式的函數(shù)式特性:索引示例
.NET Lambda表達式最節(jié)省的部分
使用Lambda表達式還可以節(jié)省許多代碼(相信您從第一個示例中也可以看出來了)。不過我認為,最省代碼的部分更應(yīng)該可能是其“分組”和“字典轉(zhuǎn)化”等功能。因此,我們來看下一個示例。
這個示例可能更加貼近現(xiàn)實。不知您是否關(guān)注過某些書籍后面的“索引”,它其實就是“列出所有的關(guān)鍵字,根據(jù)其首字母進行分組,并且要求對每組內(nèi)部的關(guān)鍵字進行排序”。簡單說來,我們需要的其實是這么一個方法:
- static Dictionary< char, List< string>> GetIndex(IEnumerable< string> keywords) { ... }
想想看,您會怎么做?其實不難(作為示例,我們這里只關(guān)注小寫英文,也不關(guān)心重復(fù)關(guān)鍵字這種特殊情況):
- static Dictionary< char, List< string>> GetIndex(IEnumerable< string> keywords)
- {
- // 定義字典
- var result = new Dictionary< char, List< string>>();
- // 填充字典
- foreach (var kw in keywords)
- {
- var firstChar = kw[0];
- List< string> groupKeywords;
- if (!result.TryGetValue(firstChar, out groupKeywords))
- {
- groupKeywords = new List< string>();
- result.Add(firstChar, groupKeywords);
- }
- groupKeywords.Add(kw);
- }
- // 為每個分組排序
- foreach (var groupKeywords in result.Values)
- {
- groupKeywords.Sort();
- }
- return result;
- }
那么如果利用Lambda表達式及.NET框架中定義的擴展方法,代碼又會變成什么樣呢?請看:
- static Dictionary< char, List< string>> GetIndexByLambda(IEnumerable< string> keywords)
- {
- return keywords
- .GroupBy(k => k[0]) // 按照首字母分組
- .ToDictionary( // 構(gòu)造字典
- g => g.Key, // 以每組的Key作為鍵
- g => g.OrderBy(k => k).ToList()); // 對每組排序并生成列表
- }
光從代碼數(shù)量上來看,前者便是后者的好幾倍。而有關(guān)“聲明式”,“what”等可讀性方面的優(yōu)勢就不再重復(fù)了,個人認為它比上一個例子給人的“震撼”有過之而無不及。
試想,如果我們把GetIndexByLambda方法中的Lambda表達式改成.NET 2.0中delegate形式的寫法:
- static Dictionary< char, List< string>> GetIndexByDelegate(IEnumerable< string> keywords)
- {
- return keywords
- .GroupBy(delegate(string k) { return k[0]; })
- .ToDictionary(
- delegate(IGrouping< char, string> g) { return g.Key; },
- delegate(IGrouping< char, string> g)
- {
- return g.OrderBy(delegate(string s) { return s; }).ToList();
- });
- }
您愿意編寫這樣的代碼嗎?
.NET Lambda表達式體現(xiàn)了函數(shù)式編程特性
因此,Lambda表達式在這里還是起著決定性的作用。事實上正是因為有了Lambda表達式,.NET中的一些函數(shù)式編程特性才被真正推廣開來?!罢Z言特性”決定“編程方式”的確非常有道理。這一點上Java是一個很好的反例:從理論上說,Java也有“內(nèi)聯(lián)”的寫法,但是C#的使用快感在Java那邊還只能是個夢。試想GetIndexByLambda在Java中會是什么情況3:
- public Dictionary< Char, List< String>> GetIndexInJava(Enumerable< String> keywords)
- {
- return keywords
- .GroupBy(
- new Func< String, Char> {
- public Char execute(String s) { return s.charAt(0); }
- })
- .ToDictionary(
- new Func< Grouping< Char, String>, Char> {
- public Char execute(IGrouping< Char, String> g) { return g.getKey(); }
- },
- new Func< Grouping< Char, String>, List< string>> {
- public List< String> execute(IGrouping< Char, String> g)
- {
- return g
- .OrderBy(
- new Func< String, String> {
- public String execute(String s) { return s; }
- })
- .ToList();
- }
- });
- }
一股語法噪音的氣息撲面而來,讓人無法抵擋。由于Java中的匿名類型語法(即上面這種內(nèi)聯(lián)寫法)連類型信息(new Func< String, Char>{ ... }這樣的代碼)都無法省去,因此給人非常繁瑣的感覺。面對這樣的代碼,您可能會有和我一樣的想法:“還不如最普通的寫法氨。沒錯,這種函數(shù)式編程的風(fēng)格,由于缺乏語言特性支持,實在不適合在Java語言中使用。事實上,這種內(nèi)聯(lián)寫法很早就出現(xiàn)了(至少在02、03年我還在使用Java的時候就已經(jīng)有了),但是那么多年下來一點改進都沒有。而Lambda表達式出現(xiàn)之后,社區(qū)中立即跟進了大量項目,如Moq,F(xiàn)luent NHibernate等等,充分運用了C# 3.0的這一新特性。難道這還不夠說明問題嗎?
對了,再次推薦一下Scala語言,它的代碼可以寫的和C#一樣漂亮。我不是Java平臺的粉絲,更是Java語言的忠實反對者,但是我對Java平臺上的Scala語言和開源項目都抱有強烈的好感。
既然談到了函數(shù)式編程,那么就順便再多說幾句。其實這兩個例子都有濃厚的函數(shù)式編程影子在里面,例如,對于函數(shù)試編程來說,Where常被叫做filter,Select常被叫做map。而.NET 3.5中定義的另一些方法在函數(shù)式編程里都有體現(xiàn)(如Aggregate相當(dāng)于fold)。如果您對這方面感興趣,可以關(guān)注Matthew Poswysocki提出的Functional C#類庫。
以上就介紹了.NET Lambda表達式的另一個范例。
【編輯推薦】