OOP ユニバースの出身であれば、モジュールを静的クラスに類似したものと考えると役立つでしょう。.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# はどのようにしてand演算子'a
を使用して型の 2 つのオブジェクトを比較する方法を知っているのでしょうか?<
>
舞台裏では、次のようなことを行っています。
> let gt x y = x > y;;
val gt : 'a -> 'a -> bool when 'a : comparison
Person
では、その特定のインターフェイスを実装していないタイプのオブジェクトがある場合はどうでしょうか? その場で並べ替え関数を定義したい場合はどうしますか? 1 つのアプローチは、次のように比較子を渡すことです。
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# はファンクターをサポートしていないため、厄介な回避策に頼る必要があります。上記のシナリオでは、ほとんどの場合、「ファンクター」をいくつかの補助ヘルパー関数と共にデータ構造に格納して、正しくコピーされるようにします。
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))