[英]What's the difference between functors and “generics”
我正在看OCaml的仿函數 。 它看起來與C++
/ C#
/ Java
所謂的通用對象完全相同。 如果您暫時忽略Java的類型擦除,並忽略C ++模板的實現細節(我對語言功能感興趣),仿函數對於泛型非常簡潔。 如果我理解正確,仿函數會從您提供的類型中為您提供一組新函數,例如
List<MyClass>.GetType() != List<MyOtherClass>.GetType()
但你可以大致重寫OCaml的
#module Set =
functor (Elt: ORDERED_TYPE) ->
struct
type element = Elt.t
type set = element list
let empty = []
let rec add x s =
match s with
[] -> [x]
| hd::tl ->
match Elt.compare x hd with
Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: add x tl
let rec member x s =
match s with
[] -> false
| hd::tl ->
match Elt.compare x hd with
Equal -> true (* x belongs to s *)
| Less -> false (* x is smaller than all elements of s *)
| Greater -> member x tl
end;;
進入C#
class Set<T> where T : ISortable
{
private List<T> l = new List<T>();
static public Set<T> empty = new Set<T>();
public bool IsMember(T x) {return l.IndexOf(x) > -1;}
public void Add(T x) {l.Add(x);}
}
當然,由於仿函數影響了一個Module
(它只是一堆類型的函數和值定義,類似於C#
的命名空間),所以有一點不同。
但它只是它嗎? 仿函數僅僅是泛型應用於命名空間嗎? 或者,我缺少的仿函數和泛型之間是否有任何顯着的差異。
即使仿函數只是用於命名空間的泛型,這種方法的顯着優勢是什么? 類也可以用作使用嵌套類的臨時命名空間。
但它只是它嗎? 仿函數僅僅是泛型應用於命名空間嗎?
是的,我認為可以將仿函數視為“具有泛型的命名空間”,並且在C ++中它本身會非常受歡迎,其中唯一的選擇是使用具有所有靜態成員的類,這很快就變得非常難看。 與C ++模板相比,一個巨大的優勢是模塊參數上的顯式簽名(這是我認為C ++ 0x概念可以成為的,但是oops )。
模塊與命名空間完全不同(考慮多個結構簽名,抽象和私有類型)。
即使仿函數只是用於命名空間的泛型,這種方法的顯着優勢是什么? 類也可以用作使用嵌套類的臨時命名空間。
不確定它是否符合重要條件,但可以打開名稱空間,同時明確限定類使用。
總而言之 - 我認為單獨的仿函數沒有明顯的“顯着優勢”,它只是代碼模塊化的不同方法--ML風格 - 它與核心語言非常吻合。 不確定將模塊系統與語言進行比較是否有意義。
PS C ++模板和C#泛型也是完全不同的,因此與它們作為一個整體進行比較感覺有點奇怪。
如果我理解正確,仿函數會從您提供的類型中為您提供一組新功能
更一般地說,仿函數將模塊映射到模塊。 您的Set
示例將遵循ORDERED_TYPE
簽名的模塊映射到實現集合的模塊。 ORDERED_TYPE
簽名需要類型和比較函數。 因此,您的C#不等效,因為它僅在類型上而不是在比較函數上參數化集合。 因此,您的C#只能為每個元素類型實現一個集合類型,而仿函數可以為每個元素模塊實現許多集合模塊,例如按升序和降序。
即使仿函數只是用於命名空間的泛型,這種方法的顯着優勢是什么?
高階模塊系統的一個主要優點是能夠逐步改進接口。 在OOP中,一切都是公共的或私人的(或有時受到保護或內部等)。 使用模塊,您可以逐步優化模塊簽名,使更多的公共訪問更接近模塊的內部,並在您從代碼的這一部分進一步抽象時越來越多地抽象出來。 我發現這是一個相當大的好處。
與OOP相比,高階模塊系統發光的兩個例子是相互參數化數據結構實現並構建可擴展圖庫。 有關在其他數據結構上參數化的數據結構的示例,請參閱Chris Okasaki博士論文中的 “結構抽象”部分,例如將隊列轉換為可連接列表的仿函數。 有關使用仿函數的可擴展和可重用圖算法的示例,請參閱OCaml優秀的ocamlgraph庫和使用ML Functors 設計通用圖庫的文章。
類也可以用作使用嵌套類的臨時命名空間。
在C#中,您無法對其他類的參數化進行參數化。 在C ++中,您可以執行一些操作,例如從通過模板傳入的類繼承。
此外,你可以咖喱仿函數。
SML中的函數是生成的,因此在程序中的某一點上應用函數生成的抽象類型與在同一應用程序中生成的抽象類型(即相同的函子,相同的參數)在另一點上不同。
例如,在:
structure IntMap1 = MakeMap(Int)
(* ... some other file in some other persons code: *)
structure IntMap2 = MakeMap(Int)
您不能在IntMap1中獲取由函數生成的映射,並將其與IntMap2中的函數一起使用,因為IntMap1.t是與IntMap2.t不同的抽象類型。
在實踐中,這意味着如果您的庫有一個生成IntMap.t的函數,那么您還必須提供IntMap結構作為庫的一部分,如果您的庫的用戶想要使用他自己的(或其他庫)IntMap,那么他有將IntMap中的值轉換為IntMap - 即使它們已經在結構上等效。
另一種方法是讓您的庫本身成為一個仿函數,並要求庫的用戶使用他們選擇的IntMap來應用該仿函數。 這也要求圖書館的用戶做更多的工作而不是理想的工作。 特別是當您的庫不僅使用IntMap,還使用其他類型的Map,以及各種Set等。
使用泛型,OTOH,編寫生成Map的庫非常容易,並且該值可以與其他帶Map的庫函數一起使用。
我剛剛找到了一個可以幫助你解決問題的來源 - 因為OCaml對於仿函數有不同的含義:
仍然 - 如果同一個詞被用於不同的概念,我會覺得很困惑。
我不知道OCaml是否有不同的含義 - 但通常Functor是一個“Function對象”(見這里: http : //en.wikipedia.org/wiki/Function_object )。 這與泛型完全不同(請參閱此處: http : //en.wikipedia.org/wiki/Generic_programming )
函數對象是可以用作函數的對象。 泛型是一種參數化對象的方法。 泛型是一種與遺傳正交的(它專門用於對象)。 仿制葯引入了類型安全性並減少了對鑄造的需求。 函子是一個改進的函數指針。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.