Есть ли в F # общая арифметическая поддержка? - PullRequest
19 голосов
/ 09 февраля 2010

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

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

Ответы [ 4 ]

8 голосов
/ 09 февраля 2010

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

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

#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> после регистрации операций.

4 голосов
/ 09 февраля 2010

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

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 '( + )'"

4 голосов
/ 09 февраля 2010

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

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

См. Также «статический член».часть «Ответы» и «моделирование классов типов»:

Функции с общими типами параметров

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

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

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

2 голосов
/ 09 февраля 2010

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

Типовые классы - секретный соус

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

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

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

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

Классы типов позволяют (помимо прочего) выполнять такого рода ограничения для всех параметров метода, а не только для первого параметра.

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

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

...