你能解释一下OCaml仿函数吗?

Bub*_*a88 17 f# ocaml functor

可能重复:
在功能编程中,什么是仿函数?

我对OCaml了解不多,我已经研究了F#一段时间并且非常了解它.

他们说F#错过了函数模型,它存在于OCaml中.我试图弄清楚究竟是什么算子,但维基百科和教程对我没什么帮助.

你能为我阐明这个谜吗?提前致谢 :)

编辑:

对于帮助过我的每个人,我都明白了这一点.您可以将问题完全复制为:在函数式编程中,什么是仿函数?

Jul*_*iet 31

如果您来自OOP Universe,那么将模块视为类似于静态类可能会有所帮助.与.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)
Run Code Online (Sandbox Code Playgroud)

好又花花公子.但是F#如何知道如何'a使用<>运算符比较两个类型的对象?

在幕后,它做了这样的事情:

> let gt x y = x > y;;

val gt : 'a -> 'a -> bool when 'a : comparison
Run Code Online (Sandbox Code Playgroud)

好吧,如果你有一个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)
Run Code Online (Sandbox Code Playgroud)

可以工作,但如果您正在使用插入,查找,删除等编写树操作模块,则需要客户端在每次调用任何内容时都会传递一个排序函数.

如果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)
Run Code Online (Sandbox Code Playgroud)

仍然在假设的语法中,您将创建您的模块:

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
Run Code Online (Sandbox Code Playgroud)

不幸的是,F#不支持仿函数,所以你必须依赖一些凌乱的解决方法.对于上面的场景,我几乎总是将"functor"存储在我的数据结构中,并带有一些辅助辅助函数,以确保它被正确复制:

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))
Run Code Online (Sandbox Code Playgroud)

  • @Daniel:这基本上就是重点.OCaml仿函数与将某种对象传递给模块的构造函数基本相同.但是由于F#modules == .NET静态类,并且.NET静态构造函数不接受其构造函数中的参数,因此需要跳过箍来获得相同类型的功能. (6认同)
  • 谢谢你这个有用的例子.你已经设法在没有一行Ocaml代码的情况下做到了,呵呵)) (4认同)
  • 这与接受IEqualityComparer的实例(例如,在构造函数中)有什么不同?它可以使用对象表达式临时实现. (4认同)

ygr*_*rek 12

函数是由模块参数化的模块,即从模块到模块的反射(普通函数是从值到值的反射,多态函数是从类型到普通函数的反射).

另请参见有关模块的ocaml-tutorial.

手册中的示例也很有用.

  • 据我所知,OO语言中的"泛型"与F#和OCaml中已有的参数多态性更相似(例如:'a list).当抽象类型不够时,参数多态性是不够的(例如:将集合构建为二叉树,您需要对元素进行排序功能). (8认同)
  • 这是对OCaml仿函数**的一个很好的解释,它们对*的好处是编程通用数据结构(参见OCaml标准库中的Set的实现,它可能是仿函数的最简单的例子)并且通常提供一个高效,静态类型检查的替代解决方案,您可以通过面向对象的编程解决许多问题. (2认同)