2010-02-09 6 views
18

Есть ли у F # такая же проблема, как у C#, где вы не можете напрямую использовать арифметические операторы с общими типами T?Есть ли у F # общая арифметическая поддержка?

Можете ли вы написать общую функцию Sum, которая вернет сумму любого значения, которое поддерживает арифметическое дополнение?

ответ

4

F # имеет ограниченную поддержку для этого. Хорошее общее решение, вероятно, включает классы типов, которые не поддерживаются CLR вообще или F # в частности.

F # имеет перегруженные арифметические операторы, использующие функции «статические ограничения членов» и «встроенные» функции. Это магия, которая позволяет, например, оператор + для работы как с int s, так и с float с. Вы можете написать функции inline, реализации которых основаны на встроенных математических операторах и сделать некоторый прогресс, но в общем случае это нетривиально. Вы можете проверить, например. исходный код Array.sum (в файле array.fs в FSharp.Core) в исходном дистрибутиве F #, который поставляется вместе с CTP, чтобы получить представление.

Смотрите также «статические ограничения члены» и «имитировать классы типов» часть этого ответа:

Functions with generic parameter types

, а также различные биты библиотеки как

http://msdn.microsoft.com/en-us/library/ee370581(VS.100).aspx

http://msdn.microsoft.com/en-us/library/ee340262(VS.100).aspx

2

Лучший механизм, который я знаю для выполнения Общая арифметика - это классы типов, к сожалению, ни C#, F #, ни время выполнения .NET в общей поддержке. Тем не менее, вы можете моделировать их самостоятельно вручную, как уже упоминалось в этом блоге:

Type Classes Are The Secret Sauce

Этот метод должен работать в C# 2.0 или более поздней версии (с использованием анонимных делегатов/лямбды).

Часто люди обращаются к интерфейсам, но работать в паре проблем

  1. Вы не можете объявить, что существующий тип реализовать интерфейс, так что вы не можете определить экземпляр этого интерфейса для встроенных типов, как межд ,
  2. Интерфейсы не могут ограничивать тип других аргументов методам.

Интерфейс объявляет, что для всех реализаций все методы на этом интерфейсе принимают одинаковый неявный тип этого «этого». Если Foo реализует некоторый интерфейс, то, очевидно, для этого выполнения параметр 'this' должен иметь тип Foo. Но нет никакого способа потребовать, чтобы другие параметры метода также были типа Foo.

Type classes позволяет вам (среди прочего) выполнять это ограничение по всем параметрам метода, а не только по первому параметру.

Как упоминалось в цитированной ранее статье, вы можете моделировать классы типов, передавая таблицы функций как явные аргументы.

(Community вики: вывесит пример из этой статьи в переводе на C# здесь, но не хватило времени с многословно объяснений)

4

Вы могли бы сделать что-то вроде этого.

let inline sum<'a when 'a : (static member (+) : 'a -> 'a -> 'a)> a b = 
    a + b 

let result = sum<int> 3 4 

Однако, если я пытаюсь let result = sum 3 4 я получаю ошибку "type ambiguity inherent in the use of the operator '(+)'"

+2

Я думал, что вам нужно использовать '^ a' вместо' 'a' при использовании статических ограничений в F #, но я могу ошибаться ... –

+0

Оба работают в этом случае. – gradbot

+5

В этом конкретном случае вам даже не нужно указывать общие параметры или ограничения, потому что они могут быть выведены: 'let inline sum a b = a + b' – kvb

8

Как уже упоминалось Брайен, есть некоторые встроенные в поддержку общего arithmethic, и вы можете использовать «статические ограничения», которые позволяют определить некоторые общие выполняет функции (хотя это немного ограничено).

В дополнение к этому вы также можете использовать динамические «числовые ассоциации», которые при использовании в функции немного медленнее, но их можно использовать, например, для определения собственного вектора или типа матрицы. Вот пример:

#r "FSharp.PowerPack.dll" 
open Microsoft.FSharp.Math 

let twoTimesLarger (n:'a) (m:'a) = 
    let ops = GlobalAssociations.GetNumericAssociation<'a>() 
    let sel = if ops.Compare(n, m) > 0 then n else m 
    ops.Multiply(sel, ops.Add(ops.One, ops.One)) 

Сначала нам нужно сослаться на библиотеку F # PowerPack, которая содержит функциональные возможности. Затем определим общую функцию с сигнатурой 'a -> 'a -> 'a. Первая строка динамически получает числовые операции для работы с типом 'a (она по существу использует некоторую таблицу поиска с типом в качестве ключа). Затем вы можете использовать методы объекта числовых операций для выполнения таких операций, как умножение, добавление (Multiply, Add) и многие другие. Функция работает с любыми номерами:

twoTimesLarger 3 4 
twoTimesLarger 2.3 2.4 
twoTimesLarger "a" "b" // Throws an exception! 

При определении собственного числового типа, вы можете определить его числовые операции и зарегистрировать их с помощью GlobalAssociations.RegisterNumericAssociation. Я считаю, что это также означает, что после регистрации операций вы сможете использовать встроенные F # Matrix<YourType> и Vector<YourType>.

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