2010-07-11 60 views
7

最近和很多TimeSpans一起工作,並且需要得到總和&的平均值。
但是,TimeSpan既不定義運算符get_Zero也不定義DivideByInt,因此Seq.sum和Seq.average不能直接用於此類型。下面無法編譯:現有的類型可以擴展到Seq.sum等嗎?

open System 
type System.TimeSpan 
    with 
     static member Zero with get() = TimeSpan() 
     static member (/) (n:DateTime, d:int) = DateTime(n.Ticks/(int64) d) 

let ts = [ TimeSpan(10L); TimeSpan(99L) ] 
let sum = ts |> Seq.sum 
let avg = ts |> Seq.average 
  • 錯誤:類型「時間跨度」不支持名爲「get_Zero」
  • 錯誤任何運營商:類型「時間跨度」不支持名爲「DivideByInt」任何運營商
  • 警告:擴展成員不能提供操作符重載。考慮將運算符定義爲類型定義的一部分。

是否有一些F#魔術可以在現有類型上定義這些運算符?

我知道下面的工作(應該是更有效地啓動),但我仍然好奇上面這樣我就可以把它添加到我的工具箱與其他類型的使用。

let sum = TimeSpan(ts |> Seq.sumBy (fun t -> t.Ticks)) 
let avg = TimeSpan(let len = ts |> Seq.length in sum.Ticks/int64 len) 
+0

'System.TimeSpan'是密封的,所以你不能繼承它。 – gradbot 2010-07-12 15:17:54

回答

9

據我所知,靜態成員的約束(即用類似Seq.sum功能使用)無法發現被類型擴展名(基本,擴展方法)添加的成員,所以我不認爲有一個直接的方法來做到這一點。

我能想到的最好的辦法是創造圍繞System.TimeSpan結構的簡單包裝。然後你可以定義所有需要的成員。該代碼是這樣的:

[<Struct>] 
type TimeSpan(ts:System.TimeSpan) = 
    member x.TimeSpan = ts 
    new(ticks:int64) = TimeSpan(System.TimeSpan(ticks)) 
    static member Zero = TimeSpan(System.TimeSpan.Zero) 
    static member (+) (a:TimeSpan, b:TimeSpan) = 
    TimeSpan(a.TimeSpan + b.TimeSpan) 
    static member DivideByInt (n:TimeSpan, d:int) = 
    TimeSpan(n.TimeSpan.Ticks/(int64 d)) 

let ts = [ TimeSpan(10L); TimeSpan(99L) ] 
let sum = ts |> Seq.sum 
let avg = ts |> Seq.average 

我叫類型TimeSpan,所以它隱藏了標準System.TimeSpan類型。但是,當您需要訪問底層系統類型時,您仍然需要編寫ts.TimeSpan,所以這並不是很好。

+0

所以,簡短的答案是「你不能直接做,但可以通過代理對象。」這對我行得通。正如我所說,這對目前的問題沒有什麼大不了的,但很高興在我的口袋裏。謝謝! – 2010-07-12 20:42:53

+0

剛剛注意到一些東西....看起來像System.TimeSpan已經有一個名爲「零」的靜態只讀屬性。任何線索爲什麼F#抱怨沒有這樣的事情? – 2010-07-12 20:50:37

+2

@James:我相信'System.TimeSpan'中的'Zero'實際上是一個字段 - 而不是一個屬性 - 而F#成員約束只適用於屬性/方法。一個真實的例子表明使用字段會導致麻煩......! – 2010-07-12 23:17:12

3

以下是相當醜陋的,但它的工作原理。它有幫助嗎?我爲TimeSpan定義了一個封裝器,它可以隱式轉換回TimeSpan

type MyTimeSpan(ts : TimeSpan) = 
    member t.op_Implicit : TimeSpan = ts 
    static member (+) (t1 : MyTimeSpan, t2 : MyTimeSpan) = 
     new MyTimeSpan(TimeSpan.FromTicks(t1.op_Implicit.Ticks + t2.op_Implicit.Ticks)) 
    static member Zero = new MyTimeSpan(TimeSpan.Zero) 
    static member DivideByInt (t : MyTimeSpan, i : int) = 
     new MyTimeSpan(TimeSpan.FromTicks(int64 (float t.op_Implicit.Ticks/float i))) 

let toMyTS ts = new MyTimeSpan(ts) 

let l = [TimeSpan.FromSeconds(3.); TimeSpan.FromSeconds(4.)] 
      |> List.map toMyTS 
      |> List.average 
+0

+1用於隱式轉換的思考。 – 2010-07-12 20:47:52

+0

隱式轉換實際上給你什麼嗎?據我所知,F#忽略它們(它不會執行任何隱式轉換),所以這隻會在C#中使用類型時纔有用...... – 2010-07-12 23:18:22