Jeśli pochodzą z wszechświat OOP, to prawdopodobnie pomaga myśleć o module analogicznym do klasy statycznej. Podobne do klas statycznych .NET, moduł OCaml ma konstruktory; w przeciwieństwie do .NET, moduły OCaml mogą akceptować parametry w swoich konstruktorach. Funktor to przerażająco brzmiąca nazwa obiektu, który przekazujesz do konstruktora modułu.
Więc pomocą kanoniczny przykład binarnego drzewa, my normalnie napisać go w F # tak:
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)
cacy. Ale w jaki sposób F # wie, jak porównać dwa obiekty typu 'a
używając operatorów <
i >
?
Za kulisami jej robi coś takiego:
> let gt x y = x > y;;
val gt : 'a -> 'a -> bool when 'a : comparison
Dobrze, dobrze, co jeśli masz obiektu typu Person
który nie wykona tego konkretnego interfejsu? Co jeśli chcesz zdefiniować funkcję sortowania w locie?Jednym ze sposobów jest po prostu przejść w comparer następująco:
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)
To działa, ale jeśli piszesz moduł do operacji drzewa z wkładką, Lookup, usuwanie, itp, wymagają klienci przechodzą w funkcja zamawiania za każdym razem, gdy wywołują cokolwiek.
Jeżeli F # obsługiwane funktory, jego składnia hipotetyczny może wyglądać następująco:
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)
Jeszcze w składni hipotetycznej, można by utworzyć moduł jako taki:
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
Niestety, F # nie robi wspierają funktory, więc musicie oprzeć się pewnym nieuporządkowanym obejściom. Dla scenariusza powyżej, ja prawie zawsze przechowywać „funktor” w moim struktury danych z niektórych AUXILLARY funkcji pomocniczych, aby upewnić się, że zostanie skopiowany wokół poprawnie:
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))
Nie jestem F # ani OCaml guru więc wygrał Zrób to jako formalną odpowiedź, ale myślę, że http://blog.matthewdoig.com/?p=152 może ci pomóc w wyjaśnianiu funktorów i jak F # radzi sobie z ich nieobecnością. –
Przeczytałem najpierw ten artykuł, jest też blog.matthewdoig.com/?p=155. Jestem w połowie drogi do zrozumienia tej rzeczy :) – Bubba88