Если вы приехали из вселенной ООП, то это, вероятно, помогает думать о модуле, аналогичном статическому классу. Подобно статическим классам .NET, OCaml-модуль имеет конструкторы; в отличие от .NET, модули OCaml могут принимать параметры в своих конструкторах. Функтор - это страшное звуковое имя для объекта, который вы передаете в конструктор модуля.
Таким образом, используя канонический пример бинарного дерева, мы обычно писать в F #, как это:
type 'a tree =
| Nil
| Node of 'a tree * 'a * 'a tree
module Tree =
let rec insert v = function
| Nil -> Node(Nil, v, Nil)
| Node(l, x, r) ->
if v < x then Node(insert v l, x, r)
elif v > x then Node(l, x, insert v r)
else Node(l, x, r)
штрафа и денди. Но как F # умеет сравнивать два объекта типа 'a
с помощью операторов <
и >
?
За кулисами его делать что-то вроде этого:
> let gt x y = x > y;;
val gt : 'a -> 'a -> bool when 'a : comparison
Хорошо, хорошо, что если у вас есть объект типа Person
, который не реализует этот конкретный интерфейс? Что делать, если вы хотите определить функцию сортировки на лету?Один из подходов, просто перейти в компараторе следующим образом:
let rec insert comparer v = function
| Nil -> Node(Nil, v, Nil)
| Node(l, x, r) ->
if comparer v x = 1 then Node(insert v l, x, r)
elif comparer v x = -1 then Node(l, x, insert v r)
else Node(l, x, r)
Это работает, но если вы пишете модуль для операций с деревом вставкой, поиском, удалением и т.д., вам требуется клиенты пройти в функция упорядочения каждый раз, когда они что-либо называют.
Если F # поддерживается функторы, его гипотетический синтаксис может выглядеть следующим образом:
type 'a Comparer =
abstract Gt : 'a -> 'a -> bool
abstract Lt : 'a -> 'a -> bool
abstract Eq : 'a -> 'a -> bool
module Tree (comparer : 'a Comparer) =
let rec insert v = function
| Nil -> Node(Nil, v, Nil)
| Node(l, x, r) ->
if comparer.Lt v x then Node(insert v l, x, r)
elif comparer.Gt v x then Node(l, x, insert v r)
else Node(l, x, r)
Еще в гипотетическом синтаксиса, вы бы создать свой модуль, как например:
module PersonTree = Tree (new Comparer<Person>
{
member this.Lt x y = x.LastName < y.LastName
member this.Gt x y = x.LastName > y.LastName
member this.Eq x y = x.LastName = y.LastName
})
let people = PersonTree.insert 1 Nil
К сожалению, F # Безразлично 't поддерживающие функторы, поэтому вам придется отказаться от некоторых запутанных обходных решений. Для выше сценария, я почти всегда хранить «функтор» в моей структуре данных с некоторыми добавочными вспомогательными функциями, чтобы убедиться, что он получает скопирован вокруг правильно:
type 'a Tree =
| Nil of 'a -> 'a -> int
| Node of 'a -> 'a -> int * 'a tree * 'a * 'a tree
module Tree =
let comparer = function
| Nil(f) -> f
| Node(f, _, _, _) -> f
let empty f = Nil(f)
let make (l, x, r) =
let f = comparer l
Node(f, l, x, r)
let rec insert v = function
| Nil(_) -> make(Nil, v, Nil)
| Node(f, l, x, r) ->
if f v x = -1 then make(insert v l, x, r)
elif f v x = 1 then make(l, x, insert v r)
else make(l, x, r)
let people = Tree.empty (function x y -> x.LastName.CompareTo(y.LastName))
Я не F # ни OCaml гуру, так что я выиграл «Я делаю это как официальный ответ, но я думаю, что http://blog.matthewdoig.com/?p=152 может немного помочь вам в объяснении функторов и о том, как F # обходит их отсутствие. –
Я сначала прочитал эту статью, также есть blog.matthewdoig.com/?p=155. Я нахожусь на полпути к пониманию этой вещи :) – Bubba88