我有许多用OCaml编写的程序,其中一些使用仿函数。现在,我正在考虑用F#编写和重写代码的一部分(以获得OCaml没有的一些优势)。我害怕的一件事是在F#中编写代码,用于函数在OCaml中的作用。
例如,我们如何在F#中模仿this example from OCaml manual?
type comparison = Less | Equal | Greater
module type ORDERED_TYPE = sig
type t
val compare: t -> t -> comparison
end
module Set =
functor (Elt: ORDERED_TYPE) -> struct
type element = Elt.t
type set = element list
let empty = []
let rec add x s =
match s with
[] -> [x]
| hd::tl ->
match Elt.compare x hd with
Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: add x tl
end
module OrderedString = struct
type t = string
let compare x y = if x = y then Equal else if x < y then Less else Greater
end
module OrderedInt = struct
type t = int
let compare x y = if x = y then Equal else if x < y then Less else Greater
end
module StringSet = Set(OrderedString)
module IntSet = Set(OrderedInt)
let try1 () = StringSet.add "foo" StringSet.empty
let try2 () = IntSet.add 2 IntSet.empty
答案 0 :(得分:7)
正如您所注意到的,F#没有仿函数 - F#模块不能按类型参数化。您可以使用面向对象的语言部分(接口,泛型类和继承)在F#中获得类似的结果。
这是一个模仿你的榜样的严厉方法。
type Comparison = Less | Equal | Greater
/// Interface corresponding to ORDERED_TYPE signature
type IOrderedType<'a> =
abstract Value: 'a
abstract Compare: IOrderedType<'a> -> Comparison
/// Type that implements ORDERED_TYPE signature, different instantiations
/// of this type correspond to your OrderedInt/OrderedString modules.
/// The 't: comparison constraint comes from the fact that (<) operator
/// is used in the body of Compare.
type Ordered<'t when 't: comparison> (t: 't) =
interface IOrderedType<'t> with
member this.Value = t
member this.Compare (other: IOrderedType<'t>) =
if t = other.Value then Equal else if t < other.Value then Less else Greater
/// A generic type that works over instances of IOrderedType interface.
type Set<'t, 'ot when 't: comparison and 'ot :> IOrderedType<'t>> (coll: IOrderedType<'t> list) =
member this.Values =
coll |> List.map (fun x -> x.Value)
member this.Add(x: 't) =
let rec add (x: IOrderedType<'t>) s =
match coll with
| [] -> [x]
| hd::tl ->
match x.Compare(hd) with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: add x tl
Set<'t, 'ot>(add (Ordered(x)) coll)
static member Empty = Set<'t, 'ot>(List.empty)
/// A helper function for Set.Add. Useful in pipelines.
module Set =
let add x (s: Set<_,_>) =
s.Add(x)
/// Type aliases for different instantiations of Set
/// (these could have easily been subtypes of Set as well)
type StringSet = Set<string, Ordered<string>>
type IntSet = Set<int, Ordered<int>>
let try1 () = Set.add "foo" StringSet.Empty
let try2 () = Set.add 2 IntSet.Empty
try1().Values
try2().Values
答案 1 :(得分:7)
这是一种使用泛型类和每种类型一个对象实现相同结果的方法。
type Comparison = Less | Equal | Greater
type Set<'a>(compare : 'a -> 'a -> Comparison) =
member this.Empty : 'a list = []
member this.Add x s =
match s with
| [] -> [x]
| hd::tl ->
match compare x hd with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: this.Add x tl
let compare x y = if x = y then Equal else if x < y then Less else Greater
let compareFloats (x : float) (y : float) = if x = y then Equal else if x < y then Less else Greater
// Note that same generic compare function can be used for stringSet and intSet
// as long as the type parameter is explicitly given
let stringSet = Set<string>(compare)
let intSet = Set<int>(compare)
// Type parameter not needed, because compareFloats is not generic
let floatSet = Set(compareFloats)
let try1 () = stringSet.Add "foo" stringSet.Empty // -> ["foo"]
let try2 () = intSet.Add 2 intSet.Empty // -> [2]
let try3 () = floatSet.Add 3.0 floatSet.Empty // -> [3.0]
答案 2 :(得分:0)
F#中的功能方式主要依赖于类型推断,从而避免使用interface
之类的OOP结构或带有member
的类型。
type Comparison = Less | Equal | Greater
type OrderedSet<'t> = 't list // type alias, not really necessary
module OrderedSet =
let empty : OrderedSet<_> = List.empty // just an empty list
let values (s : OrderedSet<_>) : OrderedSet<_> = s // identity function
let add compare x (s : OrderedSet<_>) : OrderedSet<_> =
let rec addR s =
match s with
| [] -> [x]
| hd::tl ->
match compare x hd with
| Equal -> s (* x is already in s *)
| Less -> x :: s (* x is smaller than all elements of s *)
| Greater -> hd :: addR tl
addR s
let compare x y = if x = y then Equal else if x < y then Less else Greater
let compareFloats (x : float) y = if x = y then Equal else if x < y then Less else Greater
let addGeneric v = add compare v
let addFloat v = add compareFloats v
它的用法如下:
let try1 () = OrderedSet.addGeneric "foo" OrderedSet.empty |> OrderedSet.addGeneric "bar"
let try2 () = OrderedSet.addGeneric 2 OrderedSet.empty |> OrderedSet.addGeneric 3
let try3 () = OrderedSet.empty
|> OrderedSet.addFloat 3.0
|> OrderedSet.addFloat 1.0
|> OrderedSet.addFloat 2.0
try1() |> printfn "%A" // OrderedSet<string> = ["bar"; "foo"]
try2() |> printfn "%A" // OrderedSet<int> = [2; 3]
try3() |> printfn "%A" // OrderedSet<float> = [1.0; 2.0; 3.0]
类型别名type OrderedSet<'t> = 't list
和函数empty
和values
并不是必需的,但是它们有助于掩盖实际的实现(如果需要的话)。