2014-02-02 3 views
5

Я пытаюсь сделать что-то, что было бы легко, если бы я мог использовать классы типов в F #.Вычисление пробега общего типа

Я хочу, чтобы иметь возможность вычислять текущее среднее на произвольном типе, на котором я определил сложение, скалярное умножение и скалярное деление. Это мой не-компиляции попытка до сих пор:

let updateMean<'a when 'a : (static member (*) : 'a -> float32 -> 'a) and 'a : (static member (/) : 'a -> float32 -> 'a) and 'a : (static member (+) : 'a -> 'a -> 'a)> (newObservation : 'a) (currentMean : 'a) (currentNumberOfRecords : int) = 
    (newObservation + (currentMean * (currentNumberOfRecords |> float32)))/float32 (1 + currentNumberOfRecords) 

и это сбивает с толку сообщение об ошибке:

A type parameter is missing a constraint 'when (^a or ^?766914) : (static member (+) : ^a * ^?766914 -> ^?766915)' 

ответ

8

Вы можете получить немного дальше, если вы добавите inline (что является обязательным требованием для использования статического ограничения члена) и переименовать статическую переменную от 'a до ^a (которая является синтаксисом, используемым параметрами статически разрешенных типов) и удалить явную спецификацию ограничений. Компилятор будет пытаться определить тип ограничений, основанный на коде (что делает его немного более полезной):

let inline updateMean (newObservation : ^a) (currentMean : ^a) 
         (currentNumberOfRecords : int) = 
    (newObservation + (currentMean * (currentNumberOfRecords |> float32)))/
     (float32 (1 + currentNumberOfRecords)) 

Однако, это все еще не работает, потому что вы ограничивая currentMean в float32 - в общий, компилятор требует, чтобы оба параметра оператора имели один и тот же тип. Вы можете сохранить currentNumberOfRecords как значение одного и того же типа - то только хитрая часть добавляет один, что может быть сделано с помощью LanguagePrimitives.GenericOne:

let inline updateMean newObservation currentMean currentNumberOfRecords = 
    (newObservation + (currentMean * currentNumberOfRecords))/
     (LanguagePrimitives.GenericOne + currentNumberOfRecords) 

Это работает хорошо, но я бы, вероятно, использовать несколько иной подход и сохранить общую сумму вместе с общим количеством (а затем просто разделить их, чтобы получить среднее значение - это, вероятно, имеют лучшие численные свойства, как вы избежать повторного округления):

let inline updateState (currentSum, currentCount) newObservation = 
    (currentSum + newObservation, currentCount + 1) 

let inline currentMean (currentSum, currentCount) = 
    LanguagePrimitives.DivideByInt currentSum currentCount 

хитрость заключается в том, чтобы использовать операции с LanguagePrimitives module и пусть компилятор F # определяет ограничения типа automaticall y (потому что они довольно уродливы).

Смежные вопросы