【问题标题】:How to create and use my own structure/signature in SML/NJ?如何在 SML/NJ 中创建和使用我自己的结构/签名?
【发布时间】:2019-04-28 06:43:58
【问题描述】:

我是函数式编程的新手,我想创建自己的结构/签名,称为 Dictionary。到目前为止,我在名为 dictionary-en.sml 的文件中有这个:

(* The signature DICTIONARY defines a type and a programming interface for
   the dictionary data structure. The data structure allows us to store
   data in the form of (key, value) pairs and to query the data using a key. *)
signature DICTIONARY =
sig

    (* The structure has to implement a dictionary type. It defines key type,
       which has to support equality checking, and a value type for the data
       stored in the dictionary. *)
    type (''key, 'value) dict

    (* Creates an empty dictionary. *)
    val empty: (''key, 'value) dict

    (* Returns true if a key exists in the dictionary. *)
    val exists: (''key, 'value) dict -> ''key -> bool

end

我在文件 solution.sml 中有这个:

structure Dictionary :> DICTIONARY =
struct
    type (''key, 'value) dict = (''key * 'value) list

    val empty = []

    fun exists dict key =
        case dict of
            [] => false
          | (k, _ )::rep => if k = key
                            then true
                            else exists rep key
end

但我不知道如何使用它。 当我在 REPL 中写道:

- Dictionary.exists [(3,"c"), (5, "e"), (7, "g")] 3;

我收到了这个错误:

stdIn:1.2-3.7 Error: operator and operand do not agree [tycon mismatch]
  operator domain: (''Z,'Y) Dictionary.dict
  operand:         ([int ty] * string) list
  in expression:
    Dictionary.exists ((3,"c") :: (5,"e") :: (<exp>,<exp>) :: nil)

有人可以帮帮我吗?我不知道我做错了什么。

【问题讨论】:

    标签: functional-programming sml smlnj


    【解决方案1】:

    您无法访问内部表示;整个界面由签名给出。
    您需要以某种方式添加到签名中以创建字典,而不依赖于特定结构中使用的表示。

    例如,

    val insert : (''key * 'value) -> (''key, 'value) dict -> (''key, 'value) dict
    

    会让你写

    Dictionary.exists (Dictionary.insert (3,"c") Dictionary.empty) 3;
    

    实施留作练习。

    【讨论】:

      【解决方案2】:

      在函数中

      fun exists dict key =
          case dict of
              [] => []
            | (k, _ )::rep => if k = key
                              then true
                              else exists rep key
      

      我发现了两个问题:

      • 您不能在一个地方返回 [] 而在另一个地方返回 true
      • 不要写if P then true else Q,而是写P orelse Q

      您使用的是:&gt;,这意味着模块是opaque,因此您只能访问签名中指定的内容。签名中没有提到内部列表表示,因此您不能将 dict 称为列表,即使您可能知道它是这样实现的。这是一项功能。

      我可能会将member 称为exists,因为List.exists 是一个高阶谓词,例如List.exists (fn x =&gt; x &gt; 5) [3, 6, 9]。您也可以偏离任何标准库命名,并说 containsKeycontainsValue,或类似的名称。

      除了 molbdnilo 建议的 insert 函数之外,您可能还想要一个 fromList 函数。

      这是一个重构版本(为简洁起见省略了 cmets,但我认为您的 cmets 很好!):

      signature DICTIONARY =
      sig
          type (''key, 'value) dict
      
          val empty: (''key, 'value) dict
          val member: ''key -> (''key, 'value) dict -> bool
          val insert: (''key * 'value) -> (''key, 'value) dict -> (''key, 'value) dict
          val fromList: (''key * 'value) list -> (''key, 'value) dict
      end
      
      structure Dictionary :> DICTIONARY =
      struct
          type (''key, 'value) dict = (''key * 'value) list
      
          val empty = []
      
          fun member key [] = false
            | member key ((key2, _)::dict) =
                key = key2 orelse member key dict
      
          fun insert (key, value) [] = [(key, value)]
            | insert (key, value) ((key2, value2)::dict) =
                if key = key2
                then (key, value) :: dict
                else (key2, value2) :: insert (key, value) dict
      
          fun fromList pairs = foldl (fn (pair, dict) => insert pair dict) empty pairs
      end
      

      但是由于您正在构建字典模块,因此您需要考虑两件事:

      1. 使使用某种二叉树作为内部表示成为可能,要求键可以有序,而不是比较相等性
      2. 由于标准 ML 没有像 ''key 这样的特殊语法来表示可以订购的东西(Haskell 将其概括为 type classes,但标准 ML 只有特殊语法 ''key),这是一个很好的例子用于使用 functors,这是给予高阶模块的名称,也就是参数化模块。

      这是一个您可以填写的示例签名、函子和结构:

      signature ORD = sig
        type t
        val compare : t * t -> order
      end
      
      signature DICT = sig
        type key
        type 'value dict
      
        val empty: 'value dict
        val member: key -> 'value dict -> bool
        val insert: key * 'value -> 'value dict -> 'value dict
        val fromList: (key * 'value) list -> 'value dict
      end
      
      functor Dict (Ord : ORD) :> DICT = struct
        type key = Ord.t
        type 'value dict = (key * 'value) list
      
        val empty = ...
        fun member _ _ = raise Fail "not implemented"
        fun insert _ _ = raise Fail "not implemented"
        fun fromList _ = raise Fail "not implemented"
      end
      

      此时你可以将type 'value dict改为使用二叉树,当你需要决定在这个二叉树中是向左还是向右时,你可以这样写:

      case Ord.compare (key1, key2) of
           LESS => ...
         | EQUAL => ...
         | GREATER => ...
      

      当你需要一个字典,其中键是某种特定的 orderable 类型时,你可以使用这个函子创建一个模块:

      structure IntDict = Dict(struct
                                 type t = int
                                 val compare = Int.compare
                               end)
      
      structure StringDict = Dict(struct
                                    type t = string
                                    val compare = String.compare
                                  end)
      

      更多示例请参见Standard ML functor examples

      【讨论】:

      • 我只想指出,如果你将仿函数更改为:functor Dict (Ord : ORD) :> DICT where type key = Ord.t 你可以在不透明的地方“打一个洞”字典,允许类型键,分别是 int 或 string 的别名。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 2010-09-16
      • 1970-01-01
      • 2013-11-19
      • 1970-01-01
      • 2016-02-17
      • 1970-01-01
      相关资源
      最近更新 更多