前一陣子在寫 CPU,導(dǎo)致一直沒有什么時(shí)間去做其他的事情,現(xiàn)在好不容易做完閑下來了,我又可以水文章了哈哈哈哈哈。

          有關(guān) FP 的類型部分我打算放到明年再講,因?yàn)楝F(xiàn)有的 C# 雖然有一個(gè)?pattern matching expressions,但是沒有?
          discriminated unions?和?records,只能說是個(gè)半殘廢,要實(shí)現(xiàn) FP 那一套的類型異常的復(fù)雜。西卡西,discriminated
          unions?和?records?這兩個(gè)東西官方已經(jīng)定到 C# 9 了,所以等明年 C# 9 發(fā)布了之后我再繼續(xù)說這部分的內(nèi)容。

          另外,concepts(type classes)、traits?、intersect & sum types?和高階類型也可能會隨著 C# 9、10
          一并到來。因此到時(shí)候再講才會講得更爽。另外吹一波?traits類型系統(tǒng),同樣是圖靈完備的類型系統(tǒng),在表達(dá)力上要比OOP強(qiáng)太多,歡迎大家入坑,比如 Rust
          和未來的 C#。

          這一部分我們介紹一下?Functor、Applicative和?Monad?都是些什么。

          本文試圖直觀地講,目的是讓讀者能比較容易的理解,而不是準(zhǔn)確知道其概念如何,因此會盡量避免使用一些專用的術(shù)語,如范疇學(xué)、數(shù)學(xué)、λ
          計(jì)算等等里面的東西。感興趣的話建議參考其他更專業(yè)的資料。

          Functor

          Functor 也叫做函子。想象一下這樣一件事情:

          現(xiàn)在我們有一個(gè)純函數(shù)?IsOdd
          bool IsOdd(int value) => (value & 1) == 1;
          這個(gè)純函數(shù)只干一件事情:判斷輸入是不是奇數(shù)。

          那么現(xiàn)在問題來了,如果我們有一個(gè)整數(shù)列表,要怎么去做上面這件事情呢?

          可能會有人說這太簡單了,這樣就可:
          var list = new List<int>(); return list.Select(IsOdd).ToList();
          上面這句干了件什么事情呢?其實(shí)就是:我們將?IsOdd?函數(shù)應(yīng)用到了列表中的每一個(gè)元素上,將產(chǎn)生的新的列表返回。

          現(xiàn)在我們做一次抽象,我們將這個(gè)列表想象成一個(gè)箱子M,那么我們的需要干的事情就是:把一個(gè)裝著?A?類型東西的箱子變成一個(gè)裝著?B?類型東西的箱子(A、B
          類型可相同),即?fmap函數(shù),而做這個(gè)變化的方法就是:進(jìn)入箱子M,把里面的A變成B。

          它分別接收一個(gè)把東西從A變成B的函數(shù)、一個(gè)裝著A的M,產(chǎn)生一個(gè)裝著B的M。
          M<B> Fmap(this M<A> input, Func<A, B> func);
          你暫且可以簡單地認(rèn)為,判斷一個(gè)箱子是不是?Functor,就是判斷它有沒有?fmap這個(gè)操作。

          Maybe

          我們應(yīng)該都接觸過 C# 的?Nullable<T>類型,比如?Nullable<int> t,或者寫成?int? t,這個(gè)t,當(dāng)里面的值為?null
          ?時(shí),它為?null,否則他為包含的值。

          此時(shí)我們把這個(gè)?Nullable<T>想象成這個(gè)箱子?M。那么我們可以這么說,這個(gè)M有兩種形式,一種是?Just<T>,表示有值,且值在?Just
          ?里面存放;另一種是?Nothing,表示沒有值。

          用 Haskell 寫這個(gè)Nullable<T>類型定義的話,大概長這個(gè)樣子:
          data Nullable x = Just x | Nothing
          而之所以這個(gè)Nullable<T>既可能是?Nothing,又可能是?Just<T>,只是因?yàn)?C# 的 BCL 中包含相關(guān)的隱式轉(zhuǎn)換而已。

          由于自帶的?Nullable<T>不太好具體講我們的各種實(shí)現(xiàn),且只接受值類型的數(shù)據(jù),因此我們自己實(shí)現(xiàn)一個(gè)Maybe<T>:
          public class Maybe<T> where T : notnull { private readonly T innerValue; public
          bool HasValue { get; } = false; public T Value => HasValue ? innerValue : throw
          new InvalidOperationException(); public Maybe(T value) { if (value is null)
          return; innerValue = value; HasValue = true; } public Maybe(Maybe<T> value) { if
          (!value.HasValue)return; innerValue = value.Value; HasValue = true; } private
          Maybe() { }public static implicit operator Maybe<T>(T value) => new Maybe<T>
          (value);public static Maybe<T> Nothing() => new Maybe<T>(); public override
          string ToString() => HasValue ? Value.ToString() : "Nothing"; }
          對于?Maybe<T>,我們可以寫一下它的?fmap函數(shù):
          public static Maybe<B> Fmap<A, B>(this Maybe<A> input, Func<A, B> func) =>
          inputswitch { null => Maybe<B>.Nothing(), { HasValue: true } => new Maybe<B>
          (func(input.Value)), _=> Maybe<B>.Nothing() }; Maybe<int> t1 = 7; Maybe<int> t2
          = Maybe<int>.Nothing(); Func<int, bool> func = x => (x & 1) == 1; t1.Fmap(func);
          // Just True t2.Fmap(func); // Nothing
          Applicative

          有了上面的東西,現(xiàn)在我們說說?Applicative?是干什么的。

          你可以非常容易的發(fā)現(xiàn),如果你為?Maybe<T>實(shí)現(xiàn)一個(gè) fmap,那么你可以說?Maybe<T>就是一個(gè)?Functor。

          那?Applicative?也差不多,首先Applicative是繼承自Functor的,所以Applicative本身就具有了?fmap。另外在?
          Applicative中,我們有兩個(gè)分別叫做pure和?apply的函數(shù)。

          pure干的事情很簡單,就是把東西裝到箱子里:
          M<T> Pure<T>(T input);
          那?apply?干了件什么事情呢?想象一下這件事情,此時(shí)我們把之前所說的那個(gè)用于變換的函數(shù)(Func<A, B>)也裝到了箱子當(dāng)中,變成了M<Func<A,
          B>>,那么apply所做的就是下面這件事情:
          M<B> Apply(this M<A> input, M<Func<A, B>> func);
          看起來和?fmap沒有太大的區(qū)別,唯一的不同就是我們把func也裝到了箱子M里面。

          以?Maybe<T>為例實(shí)現(xiàn)?apply:
          public static Maybe<B> Apply<A, B>(this Maybe<A> input, Maybe<Func<A, B>> func)
          => (input, func)switch { _ when input is null || func is null => Maybe<B>
          .Nothing(), ({ HasValue:true }, { HasValue: true }) => new Maybe<B>
          (func.Value(input.Value)), _=> Maybe<B>.Nothing() };
          然后我們就可以干這件事情了:
          Maybe<int> input = 3; Maybe<Func<int, bool>> isOdd = new Func<int, bool>(x =>
          (x &1) == 1); input.Apply(isOdd); // Just True
          我們的這個(gè)函數(shù)?isOdd本身可能是?Nothing,當(dāng)?input和isOdd任何一個(gè)為Nothing的時(shí)候,結(jié)果都是Nothing,否則是Just
          ,并且將值存到這個(gè)?Just里面。

          Monad

          Monad 繼承自 Applicative,并另外包含幾個(gè)額外的操作:returns、bind和then。

          returns干的事情和上面的Applicative中pure干的事情沒有區(qū)別。
          public static Maybe<A> Returns<A>(this A input) => new Maybe<A>(input);
          bind干這么一件事情 :
          M<B> Bind<A, B>(this M<A> input, Func<A, M<B>> func);
          它用一個(gè)裝在?M中的A,和一個(gè)A -> M<B>這樣的函數(shù),產(chǎn)生一個(gè)M<B>。

          then用來充當(dāng)膠水的作用,將一個(gè)個(gè)操作連接起來:
          M<B> Then(this M<A> a, M<B> b);
          為什么說這是充當(dāng)膠水的作用呢?想象一下如果我們有兩個(gè)?Monad,那么使用?then,就可以將上一個(gè)?Monad和下一個(gè)Monad
          利用函數(shù)組合起來將其連接,而不是寫為兩行語句。

          實(shí)現(xiàn)以上操作:
          public static Maybe<B> Bind<A, B>(this Maybe<A> input, Func<A, Maybe<B>> func)
          => inputswitch { { HasValue: true } => func(input.Value), _ => Maybe<B>
          .Nothing() };public static Maybe<B> Then<A, B>(this Maybe<A> input, Maybe<B>
          next) => next;
          完整Maybe<T>實(shí)現(xiàn)
          public class Maybe<T> where T : notnull { private readonly T innerValue; public
          bool HasValue { get; } = false; public T Value => HasValue ? innerValue : throw
          new InvalidOperationException(); public Maybe(T value) { if (value is null)
          return; innerValue = value; HasValue = true; } public Maybe(Maybe<T> value) { if
          (!value.HasValue)return; innerValue = value.Value; HasValue = true; } private
          Maybe() { }public static implicit operator Maybe<T>(T value) => new Maybe<T>
          (value);public static Maybe<T> Nothing() => new Maybe<T>(); public override
          string ToString() => HasValue ? Value.ToString() : "Nothing"; } public static
          class MaybeExtensions { public static Maybe<B> Fmap<A, B>(this Maybe<A> input,
          Func<A, B> func) => input switch { null => Maybe<B>.Nothing(), { HasValue: true
          } =>new Maybe<B>(func(input.Value)), _ => Maybe<B>.Nothing() }; public static
          Maybe<B> Apply<A, B>(this Maybe<A> input, Maybe<Func<A, B>> func) => (input,
          func)switch { _ when input is null || func is null => Maybe<B>.Nothing(), ({
          HasValue:true }, { HasValue: true }) => new Maybe<B>(func.Value(input.Value)), _
          => Maybe<B>.Nothing() }; public static Maybe<A> Returns<A>(this A input) => new
          Maybe<A>(input); public static Maybe<B> Bind<A, B>(this Maybe<A> input, Func<A,
          Maybe<B>> func) => input switch { { HasValue: true } => func(input.Value), _ =>
          Maybe<B>.Nothing() }; public static Maybe<B> Then<A, B>(this Maybe<A> input,
          Maybe<B> next) => next; }
          以上方法可以自行柯里化后使用,以及我調(diào)換了一些參數(shù)順序便于使用,所以可能和定義有所出入。

          有哪些常見的 Monads

          * Maybe
          * Either
          * Try
          * Reader
          * Writer
          * State
          * IO
          * List
          * ......
          C# 中有哪些 Monads

          * Task<T>
          * Nullable<T>
          * IEnumerable<T>+SelectMany
          * ......
          為什么需要 Monads

          想象一下,現(xiàn)在世界上只有一種函數(shù):純函數(shù)。它接收一個(gè)參數(shù),并且對于每一個(gè)參數(shù)值,給出固定的返回值,即?f(x)對于相同參數(shù)恒不變。

          那現(xiàn)在問題來了,如果我需要可空的值?Maybe或者隨機(jī)數(shù)Random
          等等,前者除了值本身之外,還帶有一個(gè)是否有值的狀態(tài),而后者還跟計(jì)算機(jī)的運(yùn)行環(huán)境、時(shí)間等隨機(jī)數(shù)種子的因素有關(guān)。如果我們所有的函數(shù)都是純函數(shù),那么我們?nèi)绾斡靡粋€(gè)函數(shù)去產(chǎn)生?
          Maybe?和?Random?呢?


          前者可能只需要給函數(shù)增加一個(gè)參數(shù):是否有值,然而后者呢?牽扯到時(shí)間、硬件、環(huán)境等等一切和產(chǎn)生隨機(jī)數(shù)種子有關(guān)的狀態(tài),我們當(dāng)然可以將所有狀態(tài)都當(dāng)作參數(shù)傳入,然后生成一個(gè)隨機(jī)數(shù),那更復(fù)雜的,
          IO如何處理?


          這類函數(shù)都是與環(huán)境和狀態(tài)密切相關(guān)的,狀態(tài)是可變的,并不能簡單的由參數(shù)做映射產(chǎn)生固定的結(jié)果,即這類函數(shù)具有副作用。但是,我們可以將狀態(tài)和值打包起來裝在箱子里,這個(gè)箱子即?
          Monad,這樣我們所有涉及到副作用的操作都可以在這個(gè)箱子內(nèi)部完成,將可變的狀態(tài)隔離在其中,而對外則為一個(gè)單體,仍然保持了其不變性。

          以隨機(jī)數(shù)?Random為例,我們想給隨機(jī)數(shù)加 1。(下面的代碼我就用 Haskell 放飛自我了)

          我們現(xiàn)在已經(jīng)有兩個(gè)函數(shù),nextRandom用于產(chǎn)生一個(gè)?Random Int,plusOne用于給一個(gè)?Int?加 1:
          nextRandom :: Random Int // 返回值類型為 Random Int plusOne :: Int -> Int // 參數(shù)類型為
          Int,返回值類型為 Int
          然后我們有?bind和returns操作,那我們只需要利用著兩個(gè)操作將我們已有的兩個(gè)函數(shù)組合即可:
          bind (nextRandom (returns plusOne))
          利用符號表示即為:
          nextRandom >>= plusOne
          這樣我們將狀態(tài)等帶有副作用的操作全部隔離在了 Monad 中,我們接觸到的東西都是不變的,并且滿足?f(g(x)) = g(f(x))!

          當(dāng)然這個(gè)例子使用Monad的bind操作純屬小題大做,此例子中只需要利用Functor的?fmap操作能搞定:
          fmap plusOne nextRandom
          利用符號表示即為:
          plusOne <$> nextRandom

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

                欧美老熟妇性爱 | 娇小xXXXBXBⅨ黑人XX | 办公室av | 91麻豆视频 | aaa在线免费观看 |