【函數式】Monads模式初探——Monoids

Monads是什么

知乎里有關于什么是Monad的問題討論,而在維基百科中也有關于Monad的釋義。作為初次接觸到Monads概念,難免會有些暈頭轉向,也難免會有些畏懼(因為Monads和數學中的范疇論有密切關系),但是Monads又是如此的重要,因為它在函數式編程中實在是應用太廣泛了,并且在Scala的標準庫中又常常遇到,使得我們不得不好好研究一番。

Monoids

Monoids是一種元素的集合,它需要滿足結合律和幺元(Identity,也稱為單位元,這種元和其他元素結合時,不會改變那么元素)這些約束條件。
比如:

  • 整數類型Int,其中0是Identity,其中的任何整數滿足結合律
  • 列表類型List,任何兩個列表可以通過:::連接起來,其中Nil或空列表[]是Identity
  • 字符串類型String,兩個字符串可以拼接,其中空字符串或""是Identity

Monoids在平常的編程之中無處不在,當用到一個列表,連接字符串,通過一個循環得到一個累加結果,都在使用到Monoids。

條件和定律

  1. 一個抽象類型A
  1. 一個二元結合性函數(binary associative function),對傳入的兩個A類參數進行操作后產生一個A類型結果。op操作必須是結合性的,即op(x, y) == op(y, x);op(a,op(b,c)) = op(op(a,b),c):這個定律是函數組合(function composition)不可缺的條件
  2. 一個恒等值(identity)。二元函數參數中如果有一個是恒等值時操作結果為另一個參數,即滿足op(identity, x) == x

示例

Monoid可以用下面的代碼描述:

trait Monoid[T] {
  def op(m1: T, m2: T): T
  val identity: T
}

這個特質可以被混入類型(classes)、對象(objects)或者其他特質中。請看下面的舉例:

case class StringMonoid extends Monoid[String] {
  def op(s1: String, s2: String) = s1 + s2
  val identity = ""
}

val stringMonoid = StringMonoid()
println(stringMonoid.op(stringMonoid.identity, "John"))
println(stringMonoid.op("John", "Hunt"))
// Output is
// John
// JohnHunt

object IntMonoid extends Monoid[Int] {
  def op(x: Int, y: Int) = x + y
  val identity = 0
}

println(IntMonoid.op(IntMonoid.identity, 1))
println(IntMonoid.op(1, 2))
println(IntMonoid.op(2, 1))
// Output is
// 1
// 3
// 3

Monoid和折疊

如果有一個Monoid結構和一組數據。可以通過對每個元素進行Monoid的op操作來將集合縮減為一個值,比如將一個整數列表通過元素累加的方式得到所有整數的和。
Monoid和List有著密切的聯系。在List的foldLeft操作中,用一個初始元素從列表的左邊元素開始操作,一直到對所有元素都操作完。如List("A", "B", "C").foldLeft("")(_ + _)這個對字符串列表實現累加功能,foldLeft傳入的兩個參數分別是空字符串和二元操作運算,這正好符合Monoid的定義,可以輕松利用StringMonoid代替,List("A", "B", "C").foldLeft(StringMonoid.identity)(StringMonoid.op)

結合性與并行化

Monoid的結合性意味著我們在對類似List的數據結構進行折疊的時候有很大的靈活性。我們已經知道可以使用foldLeft和foldRight對一個列表進行順序的規則(reduce)操作。但是我們同樣可以將數據分成多份,并行的進行折疊,然后利用monoid將各個部分合并起來。
左折疊操作是op(op(op(a, b), c), d)
右折疊操作是op(a, op(b, op(c, d)))
并行算法為op(op(a, b), op(c, d)),其中op(a, b)和op(c, d)是同時運算的。
如果我們對一個超大文件進行文字數統計或者尋找最大值什么的,我們可以把這個大文件分成若干小文件然后同時計算后再合計將節省很多計算時間。

Monoid模式的優缺點

優點:

  • Monoid模式提供了一種在特定場景下將元素合并的標準方法
  • 結合性的保證可以用來定義函數之間組合

缺點:

  • 并不是所有集合都可以很容易的應用Monoid模式。比如String Monoid,不同順序的字符串進行連接可能會得到不同的結果。

從范疇論到計算機編程

從Monoid到Monad,這些概念都是從范疇論中衍生出來的。
理解范疇論的一個好方法是把它理解為應用到函數式編程領域的設計模式。范疇論定義了一些非常底層的概念抽象,這些概念可以直接用Scala這樣的支持函數式編程的語言表達。在設計軟件的時候,如果一個特定實體符合其中一個概念,那么立刻就有一整組操作可用,而且包含推理其用法的方法。

范疇是由元素對象和態射箭頭組成的,這個箭頭開始端是一個元素對象,目的地也是一個元素對象。這里態射箭頭有兩種,不同元素對象比如a和b之間的態射箭頭稱為組合箭頭,而指向自己的箭頭稱為元箭頭,或者單元,幺元。

范疇的元素對象和箭頭態射的規則如下:

  1. 對于箭頭f:a -> b和箭頭g:b -> c,如果有一個箭頭h: a -> c,那么就稱為它們的組合,寫法是:h=g·f
  1. 對于每個元素對象,都有一個單元箭頭:id:a -> a。對于任何f: a -> b,滿足f·id = f;對于任何g: c -> a,滿足id·g = g
  2. 組合符合結合律:f·(g·h) = (f·g)·h
    態射箭頭有兩種,一種是標號1的組合箭頭,還有一種是標號2的單元箭頭。

我們將一個范疇有元素對象和態射箭頭,態射箭頭有組合和幺元兩種,且滿足結合律,這種范疇稱為Monoid。

對于某非空集合S,若存在S上的二元運算"*"使得對于任意的a,b∈S,有a*b∈S(運算封閉),則稱{S,*}為廣群。 廣群只是定義一個集合,集合中有元素和操作,操作結果也屬于這個集合,這樣泛泛的集合稱為廣群。 如果廣群再加上結合律約束,就會得到半群,因此半群是廣群的子集,要求更苛刻些,而半群如果再加上幺元(identity element)就是幺半群,也就是結合律+幺元=幺半群,所以,Monid對應的中文是幺半群。

參考資料

Functional Programming in Scala
Scala Design Patterns: Patterns for Practical Reuse and Design
什么是Monoid?
我所理解的monad(2):fold與monoid

轉載請注明作者Jason Ding及其出處
Github博客主頁(http://jasonding1354.github.io/)
GitCafe博客主頁(http://jasonding1354.gitcafe.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡書主頁(http://www.lxweimin.com/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進入我的博客主頁

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容

  • 單子 單子(Monad)是一種將函子組合應用的方法。在計算機科學里,單子經常用來代表計算(computation)...
    JasonDing閱讀 1,762評論 0 2
  • 背景 所有一切的開始都是因為這句話:一個單子(Monad)說白了不過就是自函子范疇上的一個幺半群而已,有什么難以理...
    福克斯記閱讀 13,924評論 6 65
  • 看電影,甚至于看同一部電影,對不同人而言也是具有雙重屬性的。對絕大多數人而言,它是“娛樂的”甚至“乏味的”,而對一...
    江寒園閱讀 8,701評論 63 327
  • 成績出來的時候總是我 沮喪的時刻 回過頭來看過去幾年之中,和應試各種考試作戰,這樣的情緒一直貫穿沒有終止過,無論是...
    磬舟曄曄閱讀 209評論 0 0
  • 雨又一次飄零 蒙蒙的夜空 細雨還是淋濕了我的心靈 是那一場驀然回首的日子 我依舊錯過了往日的光環 陽光海岸 但我依...
    米瀾盛若閱讀 224評論 0 5