<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      理解函數式編程中的函數組合--Monoids(二)

      使用函數式語言來建立領域模型--類型組合
      理解函數式編程語言中的組合--前言(一)

      理解函數式編程中的函數組合--Monoids(二)

      繼上篇文章引出《范疇論》之后,我準備通過幾篇文章,來介紹函數式編程語言中的若干"行話",例如Functor, Applicative, Monad。如果給這些名字一個通俗的名稱,我覺得Combinator(組合子)比較形象一些,組合子可以將函數組合起來。我在一篇文章中還看到過一個另一個通俗的說法--“膠水函數”,簡而言之就是如果兩個函數與不能夠直接組合,那么就可以通過一種像膠水一樣的函數,把兩個函數粘接起來,從而達到組合函數的目的。

      在正式講解這些概念之前,我想提一下“行話”這一現象,其實不光是函數式編程領域,OO設計里也有不少“行話”或者說“術語“,例如,”依賴注入“, ”多態“, ”橋接模式“,這些詞大家聽著都不陌生,源于大家對OO的長期實踐。但是如果摒棄偏見,理解并靈活應用這些概念并不是一蹴而就的。有時候你覺得簡單,只是因為更熟悉而已。

      這篇文章為大家介紹《范疇論》里的一個基礎概念-Monoids(注意,不是Monad)。另外本文的例子都通過TypeScript來描述,另外本文的術語都會保持英文名稱,因為這些術語翻譯為漢語價值不大,另外保持英文名稱也方便大家搜索相關介紹。

      Monoids

      首先Monoids一詞來源于數學家,翻譯成中文沒有任何意義,你不會從中文翻譯里面得到任何關于Monoids含義的線索,如果非要給他一個中文翻譯,我會翻譯為”可聚合的事物"。當你理解了Monoids, 你就會發現在生活中,處處存在著Monoids。 只不過數學家善于歸納總結,給與了這一類事物一個確切的定義和相應的定律。

      讓我們還原一下數學家發現這類事物的場景:

      可聚合的事物

      1 + 2 = 3
      

      這行數學運算可以描述為:兩個“數字”通過“相加”運算,得到了一個結果,其結果任然為“數字”。

      "a" + "b" = "ab"
      

      上面這行運算可以描述為:兩個"字符“通過”拼接“操作,得到了一個結果,其結果任然為”字符串“。
      如果我們將上面的這兩個運算進一步泛化,就會得到類下面的模式(pattern):

      • 有兩個事物
      • 兩個事物能夠通過一種組合方式將他們組合起來
      • 得到的事物跟之前的類型是一致的

      這個規律能夠運用在非數字或者字符串之外的其他事物上面嗎?假如這種事物可以通過某種方式組合到一起,是不是就能夠符合這一規律呢?
      錢算不算?

      type Money = {
        amount: number
      };
      
      const a: Money = { amount: 10.2 }
      const b: Money = { amount: 2.8 }
      const c: Money = { amount: a.amount + b.amount }
      

      你如果熟悉DDD中提到的ValueObject,你可以將這模式應用在很多事物(ValueObject)上。
      為什么這個模式要強調組合之后的事物跟之前的類型是一致的(closure)?
      因為你可以把這個模式推廣到list上
      換句話說,如果一個二元運算如果返回的類型跟之前一致,就可以把這個操作符應用到一個list上,這個函數叫做reduce。

      [0, 2, 3, 4].reduce((acc, val) => acc + val);
      ["a", "b", "c", "d"].reduce((acc, val) => acc + val);
      

      MapReduce大家應該都不陌生,為什么叫Map? 因為需要將數據轉化為Monoids, 為什么要Reduce? 因為需要聚合數據。

      結合律

      實際上,只符合上面的模式,還不能稱之為為Monoids, 確切的說叫做Magma。我們小學數學都學習過結合律(Associative),注意不是交換律(commutative),例如:

      (1 + 2) + 3 = 1 + (2 + 3) = 6
      

      結合律說左右組合順序不重要,得到的結果都是一樣的,這一定律實際上對事物組合的運算符做出了限制,例如,針對數字運算,乘法符合結合律嗎?

      (1 * 2) * 3 = 1 * (2 * 3) = 6
      

      答案是符合,那么除法和減法呢?

      (1 - 2) - 3 != 1 - (2 - 3)
      (1 % 2) % 3 != 1 % (2 % 3)
      

      除法和減法不符合結合律,為什么結合律這么重要?
      因為當順序不是問題的時候,并行計算和累加就顯得輕而易舉。因為執行順序不是問題,你就可以把計算量分配到若干個機器上,然后累加結果。或者說今天計算了任務的30%,等明天啟動任務的時候接著計算,而不需要重新計算整個數據集。

      Identity元素

      目前為止,得到的事物叫Semigroups,只差最后一個條件便可稱之為Monoids。看下面的運算:

      1 + 0 = 1
      "a" + "" = "a"
      

      有什么規律呢?針對數字和”加法“運算,任何數字加0,得到的結果跟之前一樣。針對字符串和”加法“運算,任何字符串和”空字符串“拼接起來,得到的結果也跟之前一樣。
      對于數字和”乘法“運算來說,0元素是1:

      10 * 1 = 10
      

      對于list而言,0元素是空list:

      const a = [1, 2, 3]
      const b: number[] = []
      const c = [...a, ...b]
      
      expect(c).toEqual(a);
      

      數學家把這個類似于0一樣的元素稱之為identity元素,為什么需要identity元素呢?
      試想一下如何對一個空數組做reduce?

      const a: number[] = [];
      const result = a.reduce((acc, val) => acc + val);
      

      這行代碼會報錯,reduce函數會抱怨你沒有提供一個初始值,而這個不影響計算結果的初始值,實際就是identity元素。

      const result = a.reduce((acc, val) => acc + val, 0);
      

      大部分語言把提供初始值的函數稱之為fold函數。不過fold的基礎并不是Monoids, 而是Catamorphisms,在此不再細說。

      Monoids定律

      數學家將上面的三個規律定義為三個定律(laws):

      • 定律1 (Closure): 兩個事物合并后總能得到相同類型的事物。
      • 定律2 (Associativity): 當組合一組事物時,組合的順序不會影響結果(不是交換律的那種順序)。
      • 定律3 (Identity element): 有一個0元素,任何事物跟0元素合并之后的結果不變。
        用數學家的話說,凡是符合上面三個定律的事物被稱之為Monoids。符合定律1的叫做Magma, 同時符合定律1和定律2的稱之為Semigroups。

      用一句話概括,Monoids是一個能夠滿足結合律,擁有Identity元素的二元運算。如果用代碼來定義,大概如下:

      interface Monoid<A> {
        readonly concat: (x: A, y: A) => A
        readonly empty: A
      }
      

      結合律則要滿足下面的等式:

      concat(x, concat(y, z)) = concat(concat(x, y), z)
      

      上面用來描述Monoids的方式,在函數式編程語言里叫做type classes。嚴格來說,TypeScript原生并不支持type classes,也不支持Higher Kinded Types(HKT), 上面的例子只是我們用interface來模擬了一個type classes定義。
      對于原生支持type classes的語言,例如Haskell, Monoid被定義為:

      class Monoid m where  
          mempty :: m  
          mappend :: m -> m -> m  
          mconcat :: [m] -> m  
          mconcat = foldr mappend mempty
      

      讓我們對這個定義做個簡單分析,首先,m這種類型可以作為Monoid實例,只要符合:

      • mempty代表Identty 元素
      • mappend代表一個函數,接受兩個相同類型的參數,然后返回一個類型也一樣的值,可以理解為二元操作符
      • mconcat是一個函數,接受一組monoid值,然后聚合為一個值。它擁有一個默認實現,使用mappend操作符和mempty作為默認值,來fold一個列表

      可以看出Haskell里面的的type class基本跟我們在TypeScript里用interfaced定義出來的type class差不多,實際上是不是原生支持Type classes,并不影響TypeScript可以作為一門函數式編程語言,類似的語言還有F#等。

      函數也可以是Monoids

      大家要明白《范疇論》的抽象程度很高,Monoid并不單單指我們在文章中提到的字符串,數字之類,它可以是宇宙中的任何符合Monoids law的事物,這個事物也可以是函數。在TypeScript里,定義一個具有一個參數和返回類型的函數如下:

      type func = <T1, T2>(x: T1) => T2
      

      這個函數的簽名如下:

      T1 -> T2
      

      在一個函數a->b中,如果b是monoid,那么這個函數也是一個monoid。也就是說函數簽名相同的兩個函數是可以組合的。相關過程我不再證明,在Haskell里,這樣的一條規則可以被描述為:

      instance Monoid b => Monoid (a -> b)
      

      特別的,當函數是一個monoid并且其輸入類型和輸出類型一致時,被稱為Endomorphism monoid。

      type func = <T>(x: T) => T
      

      Monoid實戰

      如果說“數字”再加上"加法"操作符就是Monoid, 那么通過reduce就可以輕而易舉的將一堆數字累加起來。讓我們看一個稍微復雜的例子,例如在購物車里,每個商品都可以用下面的類型來表示:

      type OrderLine = {
        id: number,
        name: string,
        quality: number,
        total: number
      }
      

      用命令式的思想來匯總總價,通常就是一個for循環,然后累加結果。不過,你應該想到,Monoid就是用來解決數據的累加問題,我們可以通過reduce解決問題,你可能會想到這樣做:

      const total = orderLines.reduce((acc, val) => acc.total + val.total)
      

      這行代碼會報錯,編譯器會抱怨你在reduce函數里傳入的高階函數簽名不符合要求,因為你傳入的函數簽名如下:

      OrderLine -> OrderLine -> number
      

      這個函數不符合Monoid定律,即返回類型不是一個OrderLine類型。Reduce期望你傳入的函數類型簽名為:

      OrderLine -> OrderLine -> OrderLine
      

      我們只需要將這個高階函數的返回類型也定義為OrderLine即可,即:

      const addTwoOrderLines = (line1: OrderLine, line2: OrderLine): OrderLine => (
        {
          name: "total",
          quality: line1.quality + line2.quality,
          total: line1.total + line2.total
        }
      )
      const total = orderLines.reduce(addTwoOrderLines)
      

      結束語

      本文通過描述Monoid帶大家進入函數式編程和《范疇論》的世界,為了進一步用代碼實現這些例子,我在接下來的文章中還會引入fp-ts,從而通過TypeScript來展示一些實例。

      posted @ 2021-03-08 16:51  richiezhang  閱讀(1405)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲精品麻豆一二三区| 在线免费播放亚洲自拍网| 亚洲熟妇自偷自拍另亚洲| 成 人免费va视频| 韩产日产国产欧产| 狠狠躁夜夜躁人人爽天天古典| 夜爽8888视频在线观看| 日韩av综合免费在线| 18禁黄无遮挡网站免费| 久久永久视频| 区一区二区三区中文字幕| 好男人官网资源在线观看| 伊人久久大香线蕉AV网| 亚洲国产成人AⅤ片在线观看| 国产一区二区午夜福利久久| 久久先锋男人AV资源网站| 亚洲最大中文字幕无码网站| 在线精品自拍亚洲第一区| 国产成人精品三上悠亚久久| 色综合中文综合网| 人妻少妇乱子伦精品| 成人福利国产午夜AV免费不卡在线| 国内偷自第一区二区三区| 男人进女人下部全黄大色视频 | 欲香欲色天天天综合和网| 精品乱人伦一区二区三区| 亚洲天堂成年人在线视频| 亚洲免费人成在线视频观看| 日韩中文字幕高清有码| 久久热这里只有精品66| 亚洲av日韩在线资源| 国产乱子伦精品免费无码专区| 久久精品人人槡人妻人人玩av| 亚洲国产精品男人的天堂| 国产成人综合色视频精品| 午夜福利精品国产二区| 狠狠躁夜夜躁人人爽天天古典| 精品无码一区在线观看| 久久精品亚洲中文字幕无码网站| 国产乱妇无乱码大黄aa片| 最新偷拍一区二区三区|