函式式程式設計之 重新認識泛型 1

2021-08-22 13:13:09 字數 2741 閱讀 4471

如果問c#這門語言那些特性是非常好的設計,那麼泛型肯定是其中乙個。泛型的引入間接帶來了linq,大家大概都享受過linq帶來的快感。泛型這個特性來自於函式式語言,f#的設計者don syme參與了.net中的泛型設計。c#中的泛型特性使用起來也很簡單,以至於沒有任何函式式基礎就能把linq耍起來。本文將從函式式語言的角度來分析泛型,進而描述為什麼會有select、selectmany這樣的函式。

大家一定只用過list這個泛型型別,當然你自己一定也設計過某種泛型類,比如repository等。在函式式程式設計之-拒絕空引用異常(option型別)一文中還提到了避免nullreferenceexception的型別optional。

在函式式程式語言中,泛型的應用更加廣泛,比如你在f#中定義乙個方法:

let print x = printf "%a" x
得到的方法簽名如下:

val print : x:'a -> unit
'a表示任意型別,f#中定義的方法是自動泛化的,在c#則需要手動編寫泛型方法。

對於任意型別a,總有那麼乙個對應的泛型型別e與之對應,無論是list,還是optional等。我們把從a到e的過程叫做提公升(lifting)。在我們寫**的過程中,必然存在把a變換成e,也有把e變換成a的過程:

public optionaladd10(optionalx)

return optional.none();

}

上面的**描述了乙個向optional加10的過程,如果引數x中的optional沒有缺失,就把optional變為int,同時在int的基礎上加10,然後再轉化為optional。

用f#實現相同的邏輯:

let add10 x =

match x with

| some s -> some (s+10)

| none -> none

這看似很正常的**片段,在函式式語言裡是錯誤的思路。函式式程式語言的型別可以分為兩類,型別a和被提公升的型別e,無論e是list、optional還是其他。當**在a和被提公升型別e之間來回切換時,**就會變得異常複雜:

數學家就想使用一些固定的套路來解決這個問題。如下圖所示,你一旦擁有某個提公升型別e,就應該盡可能的讓他保持在提公升狀態。

對於上面這個問題,你已經擁有乙個被提公升的型別optional,但是你想在optional上作用乙個未被提公升的函式:x = x + 10,最終想得到乙個optional的結果。三個已知條件有兩個是提公升型別,只有函式x = x + 10是普通型別。如果存在乙個函式,能夠接受乙個提公升型別e和乙個普通函式a->b,並且能夠返回e,那麼我們的問題就迎刃而解。這個函式就是select,有的程式語言也叫做map或者lift。

f#在option型別中已經內建了map函式:

let add10 x = 

x |> option.map (fun x -> x + 10)

對於c#中我們自定義的optional型別,可以新增加乙個select函式:

public optionalselect(funcf)

return optional.none();

}

一旦optional型別有了select方法,就可以通過下面的方式實現在optional型別上加10的需求:

public optionaladd10(optionalx)

上面例子的函式簽名如下:

optional型別中select函式的方法簽名更加泛化一些:

進一步泛化select函式:

所以對select的另類解釋為:當你已經擁有乙個上公升的型別e,如果有乙個a->b的函式,在不將e切換回到a的情況下得到e。

上面描述的提公升型別e以及定義在e型別下的函式select共同組成了functor,那麼到底符合什麼樣的規律就被稱作是functor? 見functor laws。

[theory]

[inlinedata("")]

[inlinedata("foo")]

[inlinedata("bar")]

public void optionalobeysfirstfunctorlaw(string value)

[theory]

[inlinedata("")]

[inlinedata("foo")]

[inlinedata("bar")]

public void optionalobeyssecondfunctorlaw(string value)

C 函式式程式設計之泛型(下)

c 函式式程式設計之約束型別 每當使用泛型型別時,可以通過where字句對泛型新增約束 static void outputvalue t value where t listitem value.value 這個例子直觀地宣告了乙個約束 型別t必須與listitem相匹配。泛型型別約束t x表示t...

《C 高階程式設計》之泛型 1建立泛型類

net自從2.0版本開始就支援泛型。閒話休提,馬上來看下非泛型的簡化鍊錶類,它可以包含任意型別的物件。linkedlistnode.cs中 在鍊錶中,乙個元素引用另乙個元素,所以必須建立乙個類,將其封裝在鍊錶中,並引用下乙個物件。1 public class linkedlistnode27 8pu...

C 泛型程式設計之函式模板

模板是泛型程式設計的基礎,包括函式模板和類模板兩類 其作用是建立乙個通用函式,該函式的返回值和形參型別不具體而用乙個虛擬型別代表,達到簡化的目的 語法templatet 為通用資料型別 如要實現兩數交換的函式 int型別的寫法如 void swapint int a,int b 而如果要交換doub...