如何在SML / NJ中创建和使用我自己的结构/签名?

时间:2018-11-26 15:20:14

标签: functional-programming sml smlnj

我是函数编程的新手,我想创建自己的名为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)

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

2 个答案:

答案 0 :(得分:3)

在功能中

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

您正在使用:>,这意味着该模块是opaque,因此您只能访问签名中指定的内容。内部列表表示未在签名中提及,因此即使您可能知道实现的方式,也无法将 dict 称为列表。这是一个功能。

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

除了molbdnilo建议的insert函数之外,您可能还希望拥有fromList函数。

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

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

答案 1 :(得分:2)

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

例如,

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

让你写

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

实施留作练习。