代码之家  ›  专栏  ›  技术社区  ›  James Hugard

现有的类型可以扩展到Seq.sum等吗?

f#
  •  7
  • James Hugard  · 技术社区  · 14 年前

    最近工作的时间跨度很大,需要求和;平均值。
    但是,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
    
    • 错误:“TimeSpan”类型不支持任何名为“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 )
    
    2 回复  |  直到 14 年前
        1
  •  10
  •   Tomas Petricek    14 年前

    据我所知,静态成员约束(如 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 ,所以它隐藏了标准 类型。不过,你还是要写 ts.TimeSpan 当您需要访问底层系统类型时,这并不是很好。

        2
  •  3
  •   Mau    14 年前

    以下是相当难看,但它的工作。有帮助吗?我定义了一个包装器 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