Можно ли расширить существующие типы для работы с Seq.sum и т. Д.? - PullRequest
7 голосов
/ 11 июля 2010

В последнее время мы работаем с большим количеством 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
  • Ошибка: тип «TimeSpan» не поддерживает никаких операторов с именем «get_Zero»
  • Ошибка: тип «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 ]

9 голосов
/ 11 июля 2010

Насколько я знаю, статические ограничения членов (которые используются функциями, такими как 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, когда вам нужен доступ к базовому типу системы, так что это не так хорошо, как могло бы быть.

3 голосов
/ 11 июля 2010

Mhh следующее довольно уродливо, но это работает. Это помогает? Я определяю оболочку для 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...