2010-07-11 11 views
7

He estado trabajando con un montón de TimeSpans recientemente, y necesito obtener sumas & promedios.
Sin embargo, TimeSpan no define ni el operador get_Zero ni DivideByInt, por lo que Seq.sum y Seq.average no se pueden usar directamente con este tipo. La siguiente falla al compilar:¿Se pueden extender los tipos existentes para que funcionen con Seq.sum, etc.?

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 
  • de error: El tipo 'TimeSpan' no admite ningún operadores nombrados 'get_Zero'
  • de error: El tipo 'TimeSpan' no admite ningún operadores nombrados 'DivideByInt'
  • Advertencia: los miembros de la extensión no pueden proporcionar sobrecargas al operador. Considere definir al operador como parte de la definición de tipo en su lugar.

¿Hay alguna magia F # que pueda definir estos operadores en un tipo existente?

Sé que lo siguiente funcionará (y debería ser más eficiente para arrancar), pero todavía siento curiosidad por lo anterior, así que puedo agregarlo a mi caja de herramientas para usar con otros tipos.

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' está sellado por lo que tampoco puede heredar de él. – gradbot

Respuesta

9

Por lo que yo sé, las limitaciones de miembros estáticos (que son utilizados por funciones como Seq.sum) no son capaces de descubrir los miembros que se agregan por extensiones de tipos (esencialmente, los métodos de extensión), así que no creo hay una manera directa de hacer esto.

La mejor opción que puedo pensar es crear un envoltorio simple alrededor de la estructura System.TimeSpan. Luego puede definir todos los miembros requeridos. El código se vería así:

[<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 

me llamaron del tipo TimeSpan, por lo que oculta el estándar de tipo System.TimeSpan. Sin embargo, aún necesita escribir ts.TimeSpan cuando necesite acceder al tipo de sistema subyacente, por lo que esto no es tan bueno como podría ser.

+0

Por lo tanto, las respuestas cortas son "no puede hacerlo directamente, pero puede hacerlo a través de un objeto proxy". Funciona para mi. Como dije, esto no es gran cosa para el problema actual, pero es bueno tenerlo en el bolsillo. ¡Gracias! –

+0

Acaba de notar algo ... se parece a System.TimeSpan ya tiene una propiedad estática de solo lectura llamada "Zero". ¿Alguna pista de por qué F # se queja de que no existe tal cosa? –

+2

@James: creo que 'Zero' en' System.TimeSpan' es en realidad un campo, no una propiedad, y las restricciones de los miembros F # solo funcionan con propiedades/métodos. ¡Un ejemplo de la vida real que muestra que usar campos puede causar problemas ...! –

3

Mhh lo siguiente es bastante feo, pero funciona. ¿Ayuda? Defino un contenedor para TimeSpan que se puede volver a convertir implícitamente a 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 por pensar en conversión implícita. –

+0

¿La conversión implícita realmente le da algo? Hasta donde yo sé, F # los ignora (no hace ninguna conversión implícita), así que esto sería útil solo si el tipo también se usó desde C# ... –

Cuestiones relacionadas