2011-10-08 73 views
3

所以我有這樣的:重載+運營商在F#

open System 
open System.Linq 
open Microsoft.FSharp.Collections 
type Microsoft.FSharp.Collections.List<'a> with 
    static member (+) (First : List<'a>) (Second : List<'a>) = 
     First.Concat(Second) 

let a = [1; 2; 3; 4; 54; 9] 
let b = [3; 5; 6; 4; 54] 


for x in List.(+) a b do 
    Console.WriteLine(x) 

,我想最後一行轉換成

for x in a + b do 
    Console.WriteLine(x) 

但這樣做給了我一個

The type 'int list' does not support any operands named '+' 

的文檔和在網絡上的例子是flaky,儘管我谷歌福,我一直無法得到它的工作。基本上,來自python背景,我想讓我的列表操作語法像我習慣的那樣簡潔:它不應該需要超過1個字符的中綴表示法。

回答

3

首先,覆蓋操作符應該以元組形式聲明,而不是以攜帶的形式聲明。你的情況:

type Microsoft.FSharp.Collections.List<'a> with 
    static member (+) (first: List<'a>, second: List<'a>) = 
     first.Concat(second) 

第二,你修復後,編譯器提高了"Extension members cannot provide operator overloads. Consider defining the operator as part of the type definition instead."警告。有一些解決方法已在Overload operator in F#: (/)中進行了全面討論。

+0

嗯我從來不知道這個限制;謝謝! –

+0

@LiHaoyi但有一種方法來解決這個限制,請參閱我的答案。 – Gustavo

1

我認爲運算符使用擴展方法重載不起作用。你可以做的是使用定義列表(+),一個全球性的運算符重載:

let inline (+) (f : List<'a>) (s : List<'a>) = f.Concat(s) 
+1

它可以工作,但它會覆蓋任何類型的所有(+)運算符。所以你不能再爲整數調用2 + 3。這違背了操作符重載的想法。 – pad

+0

Ops ..你是對的......奇怪的是F#編譯器沒有使用用於確定使用正確運算符的值的類型。 – Ankur

+0

就這一點而言,這是否意味着F#不會讓您使用多重簽名重載一個方法,然後在每個使用點(a.l.a. Java)中選擇正確的方法? –

8

注意@已經是一個1-字符綴運營商Concat的名單。

+0

哦,我不知道。謝謝! –

3

正如其他答案指出的那樣,您不能將+的實現添加到現有類型,因爲擴展成員被忽略並且獨立let綁定隱藏了默認(重載)實現。

如果你想使用+(這是不是真的需要,因爲F#庫中包含運營商@),你會寫爲支持直接操作F#名單包裝:

open System.Collections 
open System.Collections.Generic 

/// Wrapper for F# list that exposes '+' operator and 
/// implements 'IEnumerable<_>' in order to work with 'for' 
type PlusList<'T>(list : list<'T>) = 
    member x.List = list 
    static member (+) (first : PlusList<'a>, second : PlusList<'a>) = 
    first.List @ second.List 
    interface IEnumerable with 
    member x.GetEnumerator() = (list :> IEnumerable).GetEnumerator() 
    interface IEnumerable<'T> with 
    member x.GetEnumerator() = (list :> IEnumerable<_>).GetEnumerator() 

// Simple function to wrap list 
let pl l = PlusList<_>(l) 

let a = pl [1; 2; 3; 4; 54; 9] 
let b = pl [3; 5; 6; 4; 54] 

for x in a + b do 
    System.Console.WriteLine(x) 
6

其實是有通過使用靜態約束和重載來「重新連接」現有操作員的方式。

type ListExtension = ListExtension with 
    static member  (?<-) (ListExtension, a , b) = a @ b 
    static member inline (?<-) (ListExtension, a , b) = a + b 

let inline (+) a b = (?<-) ListExtension a b 

// test 

let lst = [1;2] + [3;4] 
// val lst : int list = [1; 2; 3; 4] 

let sum = 1 + 2 + 3 + 4 
// val sum : int = 10 

通過使用三元運算符會自動推斷靜態約束,另一個選項是創建一個方法並手工編寫約束。 第一個重載覆蓋你想添加(列表)的情況,第二個覆蓋現有的定義。

所以,現在在你的代碼,你可以這樣做:

for x in (+) a b do 
    Console.WriteLine(x) 

而且不會破壞現有(+)數字類型。