有序列表是一个列表,其中列表中稍后出现的元素永远不会小于列表中较早出现的元素,其中“小于”的概念由特定选择的关系给出。例如,列表
[1; 5; 7; 12; 13]
是相对于通常<的有序列表。关于数字的关系,列表
[17; 14; 9; 6; 2]
是相对于>的有序列表。数字上的关系但不是相对于<订单和清单
[17; 14; 9; 13; 2]
我如何定义类似于列表类型构造函数的类型构造函数olist,除了这种类型的值带有足够的信息来确定它们是否是相对于预期排序关系的有序列表?
例如,我需要一个
的功能initOList : ('a -> 'a -> bool) -> 'a olist
获取给定类型的排序关系并返回该类型的空olist。我在创建这种类型的教师时使用它来创建initOList函数?
答案 0 :(得分:1)
静态类型表示可以在编译时检查的表达式的某些属性,即,不依赖于仅在运行时可用的任何信息。因此,您无法在类型系统中真正表达依赖于运行时值的事物,例如列表长度,排序,内容等。
在您的特定情况下,您希望拥有一个采用排序并返回新类型的类型构造函数。但是,类型构造函数是类型域中的函数,即它们将类型映射到类型(即,将类型作为其参数和返回类型)。但是排序是'a -> 'a -> int
类型的函数,因此您无法在OCaml中真正表达它。
允许值出现在类型构造函数的域中的类型系统(即,使用运行时值参数化类型)称为从属类型系统。 OCaml并不真正提供依赖类型。这不是因为不可能这样做,这是一个选择问题,因为使用依赖类型系统要困难得多,而且依赖类型系统中的类型推断它是不可判定的(即,不可能写一个算法将适用于所有程序),因此通常需要用户的重要帮助来证明程序是良好类型的。因此,实现依赖类型的系统更接近自动化定理证明,例如Coq,Isabelle等。但是,有些更接近于传统编程语言,例如{{ 3}}
现在,我们很清楚,在OCaml中,类型不能使用运行时值进行参数化。然而,打字系统仍然提供足够的能力来表达排序列表的概念。我们无法真正使用类型系统检查我们的排序算法是否正确,但我们可以告诉它,嘿,这个函数确保列表已排序,只相信我们,并且把它视为理所当然。我们可以使用模块级抽象来表示这一点,例如,
module SortedList : sig
type 'a t
val create : ('a -> 'a -> int) -> 'a list -> 'a t
end = struct
type 'a t = 'a list
let create = List.sort
end
我们的想法是,我们的类型'a SortedList.t
只能使用SortedList.create
函数构建,并且没有其他方法可以解决它。因此,类型为'a SortedList.t
的表达式本身就有证明,它已被排序。我们可以使用它在类型系统中使函数的前提条件显式化,例如,假设我们有一个dedup
函数可以从列表中删除重复项,并且如果输入列表已排序,它可以正常工作。我们无法真正测试这个前提条件,因此我们可能依赖于类型系统:
val dedup : 'a SortedList.t -> 'a SortedList.t
因此,重复数据删除函数类型表明(a)它仅适用于排序列表,(b)它保留了排序。
我们的方法有两个问题。首先,我们的'a SortedList.t
过于抽象,因为它没有提供任何访问运营商。我们可以提供一个to_list
运算符,它将删除列表的排序证明,但允许对其进行所有列表操作,例如,
module SortedList : sig
type 'a t
val create : ('a -> 'a -> int) -> 'a list -> 'a t
val to_list : 'a t -> 'a list
end = struct
type 'a t = 'a list
let create = List.sort
let to_list xs = xs
end
to_list
操作是正确的,因为所有排序列表的集合是所有列表的子集。这意味着,'a SortedList.t
实际上是'a list
的子类型。很好,OCaml提供了一种通过私有抽象类型表达这种子类型关系的显式机制。
module SortedList : sig
type 'a t = private 'a list
val create : ('a -> 'a -> int) -> 'a list -> 'a t
end = struct
type 'a t = 'a list
let create = List.sort
end
类型的这种定义表明'a SortedList.t
是'a list
的子类型,因此可以将一个转换为另一个。请记住,由于向上转换在OCaml中是明确的,因此它不是自动的,因此您需要使用向上转换运算符,例如xs : 'a SortedList.t :> 'a list
。
我们实现的第二个问题是我们的'a SortedList.t
并没有真正区分不同排序的列表,即升序或降序。这对于dedup
函数来说不是问题,但是某些函数可能要求它们的输入按特定顺序排序(例如,将找到中值或任何其他统计模式的函数)。所以我们需要对订购进行编码。我们将以这种方式编码排序,我们将每个排序函数视为不同的类型。 (另一种解决方案是编码具体变体,例如Ascending
和Descending
,但我会留下这是一个练习)。我们的方法的主要缺点是我们的有序列表不再是参数类型,因为订购函数是针对特定地面类型定义的。实际上,这意味着我们的OrderedList.t现在是一个更高阶的多态类型,所以我们需要使用仿函数来实现它:
module type Ordering = sig
type element
type witness
val compare : element -> element -> int
end
module SortedList : sig
type ('a,'o) t = private 'a list
module Make(Order : Ordering) : sig
type nonrec t = (Order.element, Order.witness) t
val create : Order.element list -> t
end
end = struct
type ('a,'o) t = 'a list
module Make(Order : Ordering) = struct
type nonrec t = (Order.element, Order.witness) t
let create = List.sort Order.compare
end
end
现在,让我们对我们的实施进行一些调整。让我们提供给不同的订单:
module AscendingInt = struct
type element = int
type witness = Ascending_int
let compare : int -> int -> int = compare
end
module DescendingInt = struct
type element = int
type witness = Descending_int
let compare : int -> int -> int = fun x y -> compare y x
end
module AscendingSortedList = SortedList.Make(AscendingInt)
module DescendingSortedList = SortedList.Make(DescendingInt)
现在,让我们测试两个排序列表实际上有不同的类型:
# let asorted = AscendingSortedList.create [3;2;1];;
val asorted : AscendingSortedList.t = [1; 2; 3]
# let bsorted = DescendingSortedList.create [3;2;1];;
val bsorted : DescendingSortedList.t = [3; 2; 1]
# compare asorted bsorted;;
Characters 16-23:
compare asorted bsorted;;
^^^^^^^
Error: This expression has type
DescendingSortedList.t =
(DescendingInt.element, DescendingInt.witness) SortedList.t
but an expression was expected of type
AscendingSortedList.t =
(AscendingInt.element, AscendingInt.witness) SortedList.t
Type DescendingInt.witness is not compatible with type
AscendingInt.witness
现在让我们检查一下它们实际上是'a list
的子类型:
# compare (asorted :> int list) (bsorted :> int list);;
- : int = -1
答案 1 :(得分:0)