<ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>


      最近閑下來(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 等等。

      友情鏈接
      ioDraw流程圖
      API參考文檔
      OK工具箱
      云服務(wù)器優(yōu)惠
      阿里云優(yōu)惠券
      騰訊云優(yōu)惠券
      京東云優(yōu)惠券
      站點(diǎn)信息
      問(wèn)題反饋
      郵箱:[email protected]
      QQ群:637538335
      關(guān)注微信

        <ul id="qxxfc"><fieldset id="qxxfc"><tr id="qxxfc"></tr></fieldset></ul>
          偷拍情侣野外做爰视频 | 免费69视频 | 三区在线视频 | 日本免费一区二区三曲 | 亚洲AⅤ | 天干天干天夜夜爽 | 《深度》大尺度床戏 | 日本按摩年轻高潮4~6 | 一级大黄A片三男一女 | 中文字幕激情网 |