Есть ли в F# общая арифметическая поддержка?
Есть ли у F# та же проблема, что и в C#, где вы не можете напрямую использовать арифметические операторы с общими типами T?
Можете ли вы написать общую функцию Sum, которая бы возвращала сумму любого значения, которое поддерживает арифметическое сложение?
4 ответа
Как упоминал Брайан, есть некоторая встроенная поддержка обобщенной арифметики, и вы можете использовать "статические ограничения", которые позволяют вам самостоятельно определять некоторые обобщенные функции (хотя это немного ограничено).
В дополнение к этому вы также можете использовать динамические "числовые ассоциации", которые немного медленнее при использовании в функции, но их можно использовать, например, для определения собственного вектора или типа матрицы. Вот пример:
#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>
после регистрации операций.
Вы могли бы сделать что-то вроде этого.
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 '( + )'"
F# имеет ограниченную поддержку для этого. Хорошее общее решение, вероятно, включает классы типов, которые не поддерживаются CLR в целом или F# в частности.
В F# перегружены арифметические операторы, использующие "статические ограничения членов" и "встроенные" функции. Это магия, которая позволяет, например, +
оператор работать на обоих int
с и float
s. Ты можешь написать 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
Лучший механизм, который я знаю для выполнения обобщенной арифметики, - это классы типов, которые, к сожалению, ни C#, F#, ни среда выполнения.Net в целом не поддерживают. Тем не менее, вы можете смоделировать их вручную, как упоминалось в этом сообщении в блоге:
Этот метод должен работать в C# 2.0 или более поздней версии (с использованием анонимных делегатов / лямбд).
Часто люди обращаются к интерфейсам, но сталкиваются с парой проблем
- Вы не можете объявить, что существующий тип реализует интерфейс, поэтому вы не можете определить экземпляр этого интерфейса для встроенных типов, таких как int.
- Интерфейсы не могут ограничивать тип других аргументов методами.
Интерфейс объявляет, что для всех реализаций все методы этого интерфейса принимают один и тот же неявный тип параметра "this". Если Foo реализует некоторый интерфейс, то, очевидно, параметр "this" должен иметь тип Foo для этой реализации. Но нет способа требовать, чтобы другие параметры метода также имели тип Foo.
Классы типов позволяют (помимо прочего) выполнять такого рода ограничения для всех параметров метода, а не только для первого параметра.
Как упоминалось в цитированной ранее статье, вы можете моделировать классы типов, передавая таблицы функций в качестве явных аргументов.
(Сообщество вики: здесь будет опубликован пример из этой статьи, переведенной на C#, но не хватит времени с многословным объяснением)