最近一直在与很多TimeSpans合作,并且需要获得总和和平均值。
但是,TimeSpan既不定义运算符get_Zero也不定义DivideByInt,因此Seq.sum和Seq.average不能直接用于此类型。以下无法编译:
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
是否有一些F#magic可以在现有类型上定义这些运算符?
我知道以下内容可行(并且应该更有效地启动),但我仍然对上述内容感到好奇,所以我可以将它添加到我的工具箱中以便与其他类型一起使用。
let sum = TimeSpan( ts |> Seq.sumBy (fun t -> t.Ticks) )
let avg = TimeSpan( let len = ts |> Seq.length in sum.Ticks / int64 len )
答案 0 :(得分: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
,因此这不是很好。
答案 1 :(得分: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