SML:如何在多态函数中使用大于运算符?

时间:2015-09-29 17:08:06

标签: polymorphism sml

在SML中(具体来说,我使用的是SML / NJ)我可以编写一个简单的多态相等函数。例如:

 fun mem (x, []) = false
    | mem (x, y::l) = (x=y) orelse mem (x, l);

但是,如果我想为另一个运算符(例如大于运算符)执行相同的操作,该怎么办?例如,如果我希望以下函数能够接受Ints,Chars和Strings,那该怎么办?

 fun greater (x, []) = false
    | greater (x, y::l) = (x>y) orelse greater (x, l);

有没有办法做到这一点?

编辑:更大功能的拼写错误

2 个答案:

答案 0 :(得分:3)

假设您要使用<运算符,请执行。运算符<不是真正的多态,而是以标准ML中不可扩展的方式重载。这与=不同,它支持新类型的重载。 =的类型为''a * ''a -> bool,可以理解为:鉴于'a是一种可以进行相等性比较的类型,=需要两个这种类型的值并返回bool类型的值。当然,并非所有多态值都可以进行相等性比较。功能就是一个例子。

通过某个属性对某些多态值进行分组(例如,相等或排序的比较)的概念在Haskell中称为类型类,而标准ML的相等类型是唯一的类型标准ML支持的类。

当您查看<的类型时,它可能是int * int -> boolstring * string -> bool,或者是第三种类型,具体取决于上下文。如果标准ML支持例如,它将是整洁的'''a表示可以订购的多态值。但是这个符号在前两个或三个类型类之后变得乏味,而且一个类型在does what Haskell does之后徘徊。

最简单的替代方法是为一些具体类型t * t -> order传递一个排序函数t,其中order是值为LESS的内置类型, EQUALGREATER(看起来比bool稍微偏见,但从长远来看更好)。将这样的比较运算符传递给需要比较任意类型的所有函数时,最终会变得很烦人,因为你为很多类型和很多函数做了这个,你可以创建一个签名,

signature ORDERABLE =
sig
    type t
    val compare : t * t -> order
end

并且每当您需要对可订购的东西执行通用操作时,例如把它们放到二叉树中,就可以创建一个仿函数,

functor BinTree (SomeOrd : ORDERABLE) =
struct
    (* Let's export SomeOrd into this functor for convenience *)
    type t = SomeOrd.t
    val compare = SomeOrd.compare

    datatype tree = Leaf of t | Node of tree * t * tree

    val singleton = Leaf

    fun insert (x, Leaf) = Leaf x
      | insert (x, Node (l, y, r)) =
        case compare (x, y) of
            GREATER => Node (l, y, insert (x, r))
          | _       => Node (insert (x, l), y, r)

    fun member (x, Leaf y) = compare (x, y) = EQUAL
      | member (x, Node (l, y, r)) =
        case compare (x, y) of
            EQUAL   => true
          | LESS    => member (x, l)
          | GREATER => member (x, r)
end

可以像

一样使用
structure IntBinTree = BinTree(struct
                                   type t = int
                                   val compare = Int.compare
                               end)

val myTree = IntBinTree.insert(5, IntBinTree.insert(3, IntBinTree.singleton 4))

这里的要点是在仿函数BinTree中,所有函数都可以将订单比较运算符compare视为理所当然,因此他们不必将它们作为参数传递。缺乏多态订单比较运算符和实现自定义重载的方法,我认为这两个选项是标准ML提供的最佳选择。

答案 1 :(得分:2)

我不确定你的函数应该做什么,因为除了名字之外它与mem相同。

有两种方法:

  1. 如果您只有一个单独的函数,请将比较运算符作为参数。例如

    fun qsort leq [] = []
      | qsort leq [x] = [x]
      | qsort leq (x::xs) =
        let
            val (ys, zs) = List.partition (fn y => leq (y, x)) xs
        in
            qsort leq ys @ [x] @ qsort leq zs
        end
    
    val l = qsort op<= [3, 4, 1, 9, 0]
    
  2. 如果您处理的不仅仅是几个函数,您可以使用仿函数来整体定义整个定义模块。