【问题标题】:SML: How to use greater than operator in polymorphic functions?SML:如何在多态函数中使用大于运算符?
【发布时间】:2015-12-27 06:46:15
【问题描述】:

在 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);

有什么办法吗?

编辑:更大功能中的错字

【问题讨论】:

    标签: polymorphism sml


    【解决方案1】:

    假设您想使用< 运算符,不。运算符 < 并不是真正的多态,而是以标准 ML 中不可扩展的方式简单地重载。这与支持新类型重载的= 不同。 = 的类型是 ''a * ''a -> bool,可以理解为:鉴于 'a 是一种可以比较是否相等的类型,= 采用这种类型的两个值并返回一个 bool 类型的值. 当然,并不是所有的多态值都可以比较是否相等。函数就是一个例子。

    按特定属性(例如比较相等或排序)对某些多态值进行分组的概念在 Haskell 中称为 类型类,标准 ML 的相等类型是标准中唯一支持的类型类机器学习。

    当您查看< 的类型时,它是int * int -> boolstring * string -> bool,或者第三个,具体取决于上下文。如果支持标准 ML(例如),那就太好了。 '''a 表示可以排序的多态值。但是这个符号在前两个或三个类型类之后变得乏味并且一个徘徊和does what Haskell does

    最简单的替代方法是为某些具体类型t 传递一个排序函数t * t -> order,其中order 是具有值LESSEQUALGREATER 的内置类型(看起来比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 必须提供的最佳选择。

    【讨论】:

      【解决方案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. 如果您处理的不仅仅是几个函数,您可以使用 functor 来参数化整个定义模块。

      【讨论】:

      • 抱歉,函数中有错字。我纠正了它。是否有任何方法可以在不将运算符作为参数传递的情况下完成此操作?我用上面的例子作为一个简单的例子,但我遇到的函数定义似乎表明这是可能的。例如,在 Appel 的编译器书中,他要求读者编写一个函数,一个树搜索函数,其定义如下:lookup: 'a tree * key -&gt; 'a。除非我遗漏了某些东西(这是可能的),否则 > 或
      • @upgrayedd 我很确定你不能做你想做的事。问题是 SML 没有像 Haskell 类型类 Ord 这样可以用作推断类型的任何东西。尽管 SML 重载了&lt;,但它并没有任何与它的相等类型概念相对应的东西。
      • @upgrayedd,查找函数听起来像是实现地图类型的模块的一部分。这将是您想要使用仿函数的完美示例。那么你得到的查找函数确实会有你引用的签名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-05
      • 2012-12-17
      • 2012-11-07
      • 2014-02-03
      • 1970-01-01
      • 2013-01-19
      • 1970-01-01
      相关资源
      最近更新 更多