最近閑下來(lái)了,準(zhǔn)備出一個(gè) C# 搞 FP 的合集。本合集所有代碼均以 C# 8 為示例。
可能你說(shuō),為什么要這么做呢?回答:為了好玩。另外,意義黨們請(qǐng) gun cu ke!
?
C# 有委托,而且有 Func<> 和 Action<>,可以說(shuō)函數(shù)被視為一等功名,跟 int、bool 等類型并沒(méi)有什么區(qū)別。那么很多事情就簡(jiǎn)單了。
純函數(shù)
什么是純函數(shù)呢?純函數(shù)就是
f(x),它們接收參數(shù),得到結(jié)果,并且相同的參數(shù)得到的結(jié)果一定是相同的,用映射來(lái)說(shuō),它是滿射的。另外這個(gè)函數(shù)不會(huì)改變?nèi)魏蔚臓顟B(tài)值,它是無(wú)副作用的。
柯里化
首先,有一個(gè)東西讓我覺(jué)得不爽,那就是一般來(lái)說(shuō) C#
里的函數(shù)調(diào)用不是柯里化的,這也就意味著我沒(méi)法一個(gè)一個(gè)傳參數(shù)進(jìn)去,也沒(méi)法把傳了一部分參數(shù)的調(diào)用作為一個(gè)新函數(shù)拿去給別的地方用,那要怎么辦呢?
自己動(dòng)手,豐衣足食!
一個(gè)標(biāo)準(zhǔn)的加法函數(shù)可以這么寫(xiě):
var function = new Func<int, int, int> ((x, y) => x + y); function(1, 2); //
returns 3
如果我們想以柯里化形式調(diào)用的話,理想狀態(tài)是這么個(gè)樣子的:
function 1 2
但是這個(gè)括號(hào)我們是省不了的,所以這樣也是可以接受的:
function(1)(2);
我們看一下這個(gè)調(diào)用形式,不就是 Func<int, Func<int, int>> 嘛!so easy~
我們只需要把 Func<int, int, int> 轉(zhuǎn)化為 Func<int, Func<int, int>>:
Func<int, Func<int, int>> Currying(Func<int, int, int> f) => x => y => f(x, y);
這樣寫(xiě)就 ok 啦。進(jìn)一步改造成擴(kuò)展方法:
public static class CurryingExtensions { public static Func<int, Func<int, int
>> Currying(this Func<int, int, int> f) => x => y => f(x, y); }
于是我們只需要:
var function = new Func<int, int, int> ((x, y) => x + y) .Currying(); function(
1)(2); // returns 3
就可以采用柯里化形式調(diào)用該函數(shù)啦。
進(jìn)一步我們用泛型改造,讓柯里化適用于任何類型:
public static class CurryingExtensions { public static Func<T1, Func<T2,
TOutput>> Currying<T1, T2, TOutput>(this Func<T1, T2, TOuput> f) => x => y =>
f(x, y); }
如果遇到更多參數(shù),我們只需要給這個(gè)靜態(tài)類里面再加一個(gè)擴(kuò)展方法即可。
那 Action<> 呢?這個(gè)東西在我看來(lái)完全就是副作用,具體下方有講,我們不用他(逃
Unit
什么是 Unit 呢?Unit 就是任何函數(shù)調(diào)用后如果沒(méi)有結(jié)果,就會(huì)返回的一個(gè)東西。
可能你說(shuō),void 不就可以了?
但是如果一個(gè)純函數(shù),它沒(méi)有返回值(即 Action<>),意味著這個(gè)函數(shù)它有輸入沒(méi)輸出,那這個(gè)函數(shù)除了能用來(lái)產(chǎn)生副作用之外,就什么都干不了了。這不清真!
因此我們需要一個(gè) Unit 來(lái)代替 void,偷個(gè)懶,這個(gè) Unit 就用 ulong 來(lái)代替吧。
高階函數(shù)
什么叫做高階函數(shù),把函數(shù)當(dāng)作參數(shù)傳給另一個(gè)函數(shù),接收這個(gè)函數(shù)參數(shù)的函數(shù)就叫做高階函數(shù)。
舉個(gè)例子:f(g(x)),f 即高階函數(shù)。
假設(shè)我們現(xiàn)在要開(kāi)一個(gè)超市,超市有很多的產(chǎn)品,每種產(chǎn)品價(jià)格不同,不同產(chǎn)品可能還有各自的折扣。我們有很多種快樂(lè)水,每種快樂(lè)水價(jià)格不一樣,可口快樂(lè)水 3.5
塊,百事快樂(lè)水 3 塊,麥當(dāng)勞快樂(lè)水 9 塊,快樂(lè)水價(jià)格計(jì)算函數(shù):
var happyWater = new Func<float, int, float> ((float price, int number) =>
number * price) .Currying(); // 調(diào)用:happyWater(快樂(lè)水單價(jià))(快樂(lè)水件數(shù)); var cocaHappyWater
= happyWater(3.5f); var pepsiHappyWater = happyWater(3); var mcdHappyWater =
happyWater(9);
超市可能有折扣,A 超市不打折,B 超市打八折,計(jì)算價(jià)格函數(shù):
var calcPrice = new Func<Func<int, float>, float, int, float> ((calc,
discount, number)=> discount * calc(number)) .Currying(); //
調(diào)用:calcPrice(快樂(lè)水價(jià)格計(jì)算函數(shù))(超市折扣)(快樂(lè)水件數(shù));
現(xiàn)在我們分別在 A 超市買(mǎi)百事快樂(lè)水、B 超市買(mǎi)可口快樂(lè)水,麥當(dāng)勞的太貴了我們不買(mǎi),價(jià)格計(jì)算函數(shù)為:
var pepsiPriceCalc = calcPrice(pepsiHappyWater); var cocaPriceCalc =
calcPrice(cocaHappyWater);var priceCalcA = pepsiPriceCalc(1); // A 超市 var
priceCalcB = cocaPriceCalc(0.8f); // B 超市
最后我們?cè)?A 超市買(mǎi)了 3 瓶百事快樂(lè)水,B 超市買(mǎi)了 5 瓶可口快樂(lè)水,計(jì)算總價(jià):
var priceA = priceCalcA(3); var priceB = priceCalcB(5); var total = priceA +
priceB;
最后得到 total = 23 元。
可以看到這些函數(shù)都是可拆卸并且可以隨意組合的,而且滿足 f(g(x)) = g(f(x))。
貼上完整代碼示例:
using System; namespace ColaMarket { static class CurryingExtensions { public
static Func<T1, Func<T2, TOutput>> Currying<T1, T2, TOutput>(this Func<T1, T2,
TOutput> f) => x => y => f(x, y); public static Func<T1, Func<T2, Func<T3,
TOutput>>> Currying<T1, T2, T3, TOutput>(this Func<T1, T2, T3, TOutput> f) => x
=> y => z => f(x, y, z); } class Program { static void Main(string[] args) { var
happyWater =new Func<float, int, float> ((float price, int number) => number *
price) .Currying();var cocaHappyWater = happyWater(3.5f); var pepsiHappyWater =
happyWater(3); var mcdHappyWater = happyWater(9); var calcPrice = new Func<Func<
int, float>, float, int, float> ((calc, discount, number) => discount *
calc(number)) .Currying();var pepsiPriceCalc = calcPrice(pepsiHappyWater); var
cocaPriceCalc = calcPrice(cocaHappyWater); var priceCalcA = pepsiPriceCalc(1);
var priceCalcB = cocaPriceCalc(0.8f); var priceA = priceCalcA(3); var priceB =
priceCalcB(5); var total = priceA + priceB; Console.WriteLine(total); } } }
?
下一篇將會(huì)講更多的東西,如 Functor、Applicative 和 Monad 等等。
熱門(mén)工具 換一換
