Глобальная перегрузка операторов в F#
Я начинаю с определения собственных операторов для декартовых продуктов и умножения матриц.
Матрицы и векторы с псевдонимами в виде списков:
type Matrix = float list list
type Vector = float list
Я могу написать свой собственный код инициализации (и заключить декартово произведение в сделку), написав
let inline (*) X Y =
X |> List.collect (fun x -> Y |> List.map (fun y -> (x, y)))
let createVector size f =
[0..size - 1] |> List.map (fun i -> f i)
let createMatrix rows columns f =
[0..rows - 1] * [0..columns - 1] |> List.map (fun i j -> f i j)
Все идет нормально. Проблема в том, что мое определение *
стирает нормальное определение, хотя моя версия определена только для списков, которые не имеют своего собственного оператора умножения.
В документации http://msdn.microsoft.com/en-us/library/vstudio/dd233204.aspx говорится, что "вновь определенные операторы имеют приоритет над встроенными операторами". Однако я не ожидал, что все числовые воплощения будут уничтожены - наверняка статическая типизация позаботится об этом?
Я знаю, что можно определить глобальный оператор, который уважает типы, потому что MathNet Numerics использует *
для умножения матриц. Я также хотел бы пойти по этому пути, который потребовал бы, чтобы система печатания расставила приоритеты умножения матриц над декартовым произведением Matrix
типы (которые сами являются списками).
Кто-нибудь знает, как это сделать в F#?
2 ответа
Вот (не идиоматическое) решение:
type Mult = Mult with
static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) ->
v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
static member inline ($) (Mult, v1:'a ) = fun (v2:'a) -> v1 * v2 :'a
let inline (*) v1 v2 = (Mult $ v1) v2
Идиоматическим решением является определение Matrix
как новый тип и добавьте операторы как статические члены:
type Matrix =
| MatrixData of float list list
static member (*) (MatrixData m1, MatrixData m2) = (...)
Операторы, определенные с помощью let
полезны, когда вы знаете, что определение не конфликтует ни с чем другим, или когда вы определяете оператор локально в небольшом объеме (внутри функции).
Для пользовательских типов рекомендуется определить операторы как статические члены. Еще одним преимуществом этого подхода является то, что ваш тип будет использоваться из C# (включая операторы).
Чтобы сделать это в вашем примере, мне пришлось изменить определение типа с псевдонима типа (который не является автономным типом и поэтому не может иметь своих собственных статических членов) на однозначное распознаваемое объединение, которое может иметь членов - но это, наверное, хорошая идея в любом случае.