在使用 Base 的 OCaml 中,如何构造一个包含“int * int”类型元素的集合?

2024-02-06

在 F# 中,我只需执行以下操作:

> let x = Set.empty;;
val x : Set<'a> when 'a : comparison

> Set.add (2,3) x;;
val it : Set<int * int> = set [(2, 3)]

我知道在 OCaml 中,当使用 Base 时,我必须提供一个具有比较函数的模块,例如,如果我的元素类型是string

let x = Set.empty (module String);;
val x : (string, String.comparator_witness) Set.t = <abstr>

Set.add x "foo";;
- : (string, String.comparator_witness) Set.t = <abstr>

但我不知道如何构造一个具有类型比较函数的模块int * int。我如何构建/获得这样的模块?


要创建有序数据结构,如 Map、Set 等,您必须提供一个比较器。在 Base 中,比较器是一个一流的模块(打包到值中的模块),它提供比较函数和见证该函数的类型索引。等等,什么?接下来,让我们首先定义一个比较器。如果您已经有一个具有类型的模块

 module type Comparator_parameter = sig 
     type t (* the carrier type *)

     (* the comparison function *)
     val compare : t -> t -> int 

     (* for introspection and debugging, use `sexp_of_opaque` if not needed *)
     val sexp_of_t : t -> Sexp.t
 end

那么你可以只提供给Base.Comparator.Make函子并构建比较器

 module Lexicographical_order = struct 
    include Pair
    include Base.Comparator.Make(Pair)
 end

哪里的Pair模块提供比较功能,

 module Pair = struct
   type t = int * int [@@deriving compare, sexp_of]
 end

现在,我们可以使用比较器来创建有序结构,例如,

 let empty = Set.empty (module Lexicographical_order)

如果您不想为订单创建单独的模块(例如,因为您无法为其提供一个好的名称),那么您可以使用匿名模块,如下所示

 let empty' = Set.empty (module struct
   include Pair
   include Base.Comparator.Make(Pair)
  end)

请注意,Pair模块,传递给Base.Comparator.Make函子必须绑定在全局范围内,否则类型检查器会抱怨。这都是关于这个见证价值的。那么这个见证人是关于什么的,它见证了什么。

任何有序数据结构(例如 Map 或 Set)的语义都取决于顺序函数。比较以不同顺序构建的两个集合是错误的,例如,如果您有两个由相同数字构建的集合,但一个按升序排列,另一个按降序排列,它们将被视为不同的集合。

理想情况下,类型检查器应该防止此类错误。为此,我们需要在集合的类型中对用于构建集合的顺序进行编码。这就是 Base 正在做的事情,让我们看看empty' type,

val empty' : (int * int, Comparator.Make(Pair).comparator_witness) Set.t

and the empty type

val empty : (Lexicographical_order.t, Lexicographical_order.comparator_witness) Set.t

令人惊讶的是,编译器能够看穿名称差异(因为模块具有结构类型)并理解Lexicographical_order.comparator_witness and Comparator.Make(Pair).comparator_witness见证了相同的订单,所以我们甚至可以比较empty and empty',

# Set.equal empty empty';;
- : bool = true

为了巩固您的知识,让我们以相反的顺序构建一组对,

module Reversed_lexicographical_order = struct
  include Pair
  include Base.Comparator.Make(Pair_reveresed_compare)
end

let empty_reveresed =
  Set.empty (module Reversed_lexicographical_order)

(* the same, but with the anonyumous comparator *)
let empty_reveresed' = Set.empty (module struct
    include Pair
    include Base.Comparator.Make(Pair_reveresed_compare)
  end)

和以前一样,我们可以比较反转集的不同变体,

# Set.equal empty_reversed empty_reveresed';;
- : bool = true

但是类型检查器禁止比较不同阶的集合,

# Set.equal empty empty_reveresed;;
Characters 16-31:
  Set.equal empty empty_reveresed;;
                  ^^^^^^^^^^^^^^^
Error: This expression has type
         (Reversed_lexicographical_order.t,
          Reversed_lexicographical_order.comparator_witness) Set.t
       but an expression was expected of type
         (Lexicographical_order.t, Lexicographical_order.comparator_witness) Set.t
       Type
         Reversed_lexicographical_order.comparator_witness =
           Comparator.Make(Pair_reveresed_compare).comparator_witness
       is not compatible with type
         Lexicographical_order.comparator_witness =
           Comparator.Make(Pair).comparator_witness 

这就是比较见证人的用途,它们可以防止非常严重的错误。是的,它比 F# 需要更多的输入,但完全值得,因为它提供了来自类型检查器的更多输入,现在能够检测真正的问题。

最后几点说明。 “比较器”这个词在 Janestreet 图书馆中是一个不断发展的概念,以前它曾经意味着不同的东西。接口也在变化,比如@glennsl提供的例子有点过时了,并且使用Comparable.Make模块而不是新的和更通用的Base.Comparator.Make.

此外,有时编译器在抽象类型时将无法看到比较器之间的相等性,在这种情况下,您需要在 mli 文件中提供共享约束。您可以采取Bitvec_o​​rder https://github.com/BinaryAnalysisPlatform/bap/blob/ad79508e53e28184305fa58d51c6440ce95f47f6/lib/bitvec_order/bitvec_order.mli以图书馆为例。它展示了如何使用比较器来定义相同数据结构的各种顺序以及如何使用共享约束。图书馆文档还解释了各种术语并给出了术语的历史。

最后,如果您想知道如何启用派生预处理器,那么

  1. 对于沙丘,添加(preprocess (pps ppx_jane))您的库/可执行规范的节
  2. 对于 ocamlbuild 添加-pkg ppx_jane option;
  3. 对于顶层(例如,ocaml or utop) use #require "ppx_jane";;(如果 require 不可用,则执行#use "topfind;;",然后重复)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在使用 Base 的 OCaml 中,如何构造一个包含“int * int”类型元素的集合? 的相关文章

  • 从子模块隐式导入

    我有一个这样的包 foo init py bar py baz py 我希望在导入时自动使用子模块的功能foo So if bar py has def spam 在某个地方 我希望能够直接调用它 就像foo spam 实现这一目标的最佳方
  • 如何缩进现有 OCaml 代码

    我有大约 30 000 行缩进严重的 OCaml 代码 包括 mly 和 mll 文件 并且想要缩进它们 我尝试在谷歌上搜索 ocaml indent 的变体 我能得到的最接近的结果是使用 Omlet vim 并一次缩进一行代码 在插入模式
  • 如何使用 opam 安装特定版本的 ocaml 编译器

    如何使用 opam 或其他包管理器 安装特定版本的 ocaml 编译器 和兼容包 我快速浏览了 opam 文档 但没有找到相关信息 我需要 ocaml 编译器 最好是本机代码编译器 来构建 unison 一个文件同步软件 我需要使用相同版本
  • OCaml 中类型和模块相等的规则是什么

    我无法理解 OCaml 中模块的平等性 函子应该是适用的 这就是互联网所声称的 但这有时似乎会失败 而且我不太明白其背后的一般规则 这是我的示例代码 module type PT sig end module P struct end le
  • 如何在 OCaml 中使协变可观察

    我正在尝试为值制作一个包装器 允许调用者自行注册以获取有关它的通知 这是一些 工作 代码 module Thing sig type a t val make a gt a t val watch a gt unit gt a t gt u
  • 在 OCaml 自定义顶层设置提示

    在 OCaml 自定义顶层中 有没有一种方法可以通过编程方式设置提示 到别的东西 我希望能够更改它以响应用户的最后一个自定义功能 有点像bash你如何设置PS1 我什至找不到 directive 来更改它 谢谢 在 toplevel top
  • 链接“let”语句时使用“and”还是“in”更好?

    我意识到这可能是一个愚蠢的问题 但是 如果我把一堆let不需要需要了解彼此价值观的语句 使用是否更好and or in 例如 以下哪一个更可取 如果有 let a foo and b bar and c baz in etc or let
  • 如何在 Windows 上的 Python 2.7 上安装 Tensorflow?

    我尝试通过 pip 安装 TensorFlow pip install tensorflow 但是得到这个错误 找不到满足tensorflow要求的版本 来自版本 这个问题有解决办法吗 我还是想通过pip安装 如果您只因为 Keras 而需
  • 使用多个模块时优化 Flex

    我有一个 Flex 应用程序 加载时间非常重要 消费者网站 我希望能够在屏幕上显示一些内容 然后允许根据需要加载其他模块 我面临的问题是所有模块的总和比我将所有组件包含在单个 swf 文件中要大得多 原因很明显 例如 访问 Web 服务所需
  • OCaml 2 和 3 之间的差异

    我有兴趣学习这门语言 但似乎有关该主题的教程和书籍很少 我只找到一本关于这个主题的合适的书 用 Objective Caml 这绝对是完美的 但问题是它是基于 2 04 版本的 所以我唯一关心的是使用这本书 对于 OCaml 3 x 是否会
  • C# 中的类和模块有什么用

    有人可以解释一下类和模块之间的区别吗 你什么时候使用其中一种而不是另一种 我正在使用 C 更新 我的意思是相当于 VB 模块的 C 版本 这在很大程度上取决于您所指的 模块 Visual Basic 的模块 C 中没有真正等效的 VB Ne
  • C++ 中的函数指针与仿函数

    使用函子和函数指针有什么区别 例如 Functor struct add x int x add x int y x y int operator int y return x y Function int func int x retur
  • OCaml 文字负数?

    我在学 这是我觉得奇怪的事情 let test treeways x match x with when x lt 0 gt 1 when x gt 0 gt 1 gt 0 如果我这样称呼它 test threeways 10 我会得到类型
  • 导入 python 模块的特殊性?

    我一直在使用 python 的 PySerial 库pyserial API http pyserial sourceforge net pyserial api html 我似乎无法理解为什么我必须专门导入模块的某个部分 这会给我一个错误
  • OCaml 中的不可变变量

    我正在学习 OCaml 我对变量的不变性有点困惑 根据我正在读的书 变量是不可变的 到目前为止一切顺利 但到底为什么我可以这样做 let foo 42 let foo 4242 我缺少什么 我认为最好的解释方法是举个例子 考虑以下代码 在
  • 为现有模块创建 d.ts 文件

    我正在尝试创建一个d ts文件为React 静态容器 https github com reactjs react static container图书馆 NPM 中安装的库如下所示 var React require react var
  • 使用fold_left/right反转OCaml中的列表

    更新 解决方案 感谢 jacobm 的帮助 我想出了一个解决方案 Folding Recursion let reverse list 3 theList List fold left fun element recursive call
  • 如何在 erlang 中安装模块?

    我是 Erlang 新手 想知道如何安装第三方模块以在我的 Web 应用程序中使用 您将这些文件放在哪里以及执行什么类型的命令 如果您希望在系统范围内安装第 3 方库 例如 Mochiweb 最好将其设置在 ERL LIBS 环境变量下 我
  • 在需要时初始化模块

    我有一个模块 里面有一些初始化代码 加载模块时应执行 init 目前我正在这样做 in the module exports init function config do it in main var mod require myModu
  • 模块化大型 Grails 应用程序的最佳实践?

    我正在开发的 Grails 应用程序变得相当大 最好将其重构为几个模块 这样我们就不必每次都重新部署整个事情 将 Grails 应用程序拆分为多个模块的最佳实践是什么 特别是 我想创建一个域类 相关服务的包 并将其作为模块在应用程序中使用

随机推荐

  • Bash 脚本 - 变量内容作为命令运行

    我有一个 Perl 脚本 它给我一个定义的随机数列表 这些随机数对应于文件的行 接下来我想使用从文件中提取这些行sed bin bash count cat last queries txt wc l var perl test pl te
  • 如何通过拖动顶部的 div 来调整其大小?

    我想在拖动两个 div 之间的部分时调整 div 的大小 在搜索中我发现this http jsfiddle net gaby Bek9L 1779 但我不知道如何使这个水平而不是可用的垂直拖动 我的 div 看起来像 div div di
  • Install4j:有没有办法用包含占位符的文本覆盖欢迎消息?

    我需要覆盖install4j欢迎消息 其中包含我需要在运行时解析的占位符文本 将从属性文件中读取替换值 welcomeLabel3 Text 0 another text 1 无法向系统消息添加占位符 您必须指定整个消息 但是 您可以使用安
  • 如何从 javascript 文件(而不是 vue 组件)获取 vuex 状态

    我正在使用 vuex 2 1 1 并让事情在 vue 单文件组件中工作 然而 为了避免 vue 单文件组件中出现太多问题 我将一些函数移至utils js我将其导入到 vue 文件中的模块 在这个utils js我想阅读 vuex 状态 我
  • 初学者:AVR C++ Atmel Studio 6

    我在确定我可以访问哪些库时遇到问题 我知道我可以使用 Atmel Studio 6 IDE 用 C 对微控制器 Atmega328p 进行编程 但是 我无法弄清楚我可以访问哪些库的记录在哪里 例如 我可以使用 STL 例如向量 双端队列 吗
  • Google Maps API V3 -> 利用 MarkerCluster 但簇本身是否特定于绘制的多边形/区域?

    好吧 让我以我已经创建了很多谷歌地图的事实作为这个问题的序言 但它们是严格的标记和表示路线的折线以及一些处理程序交互 现在我希望基本上显示一张世界地图 主要是北美 我想用我拥有的一些纬度 经度将这片大陆分成我预定义的区域 使用这些区域 我想
  • CSS 面包屑箭头指向左侧

    我发现这个 css 面包屑指向右侧 我想指向左侧 相信我 我一遍又一遍地尝试 但没有成功 请有人告诉我该怎么做 div span display inline block position relative background 88b7d
  • 改造 - 更改 BaseUrl

    我有一个场景 我必须使用相同的基本 URL 调用 API 例如www myAPI com但以不同的baseUrl 我有一个 Retrofit 2 的实例 它是通过Builder return new Retrofit Builder bas
  • 将 UL 在 DIV 内垂直居中

    我有以下内容 div style background Red height 100px ul li a href Home a li ul div 我想将 ul 垂直居中在 div 中 但我不知道如何 小提琴演示 http jsfiddl
  • 如何在C++03中用sprintf正确替换sprintf_s?

    sprintf s是该函数的 Microsoft 实现sprintf他们修补了一个缺陷 添加了一个参数来获取函数限制写入的边界值 等效的引入C 11 snprintf 但在这里 我们谈论的是C 03 syntax 签名 count char
  • 为什么 Unity 会忽略非静态公共字段的初始化值?

    我在用着InvokeRepeating http docs unity3d com ScriptReference MonoBehaviour InvokeRepeating html调用游戏中的方法 我打电话InvokeRepeating
  • 在Featuretools中计算多个训练窗口的特征

    我有一张包含客户和交易的表 有没有办法获取过去 3 6 9 12 个月过滤的功能 我想自动生成功能 过去 3 个月的跨性别者数量 过去 12 个月内跨性别者数量 过去 3 个月的平均跨性别者 过去 12 个月的平均跨性别者 我尝试过使用tr
  • Kafka重启时如何让Kafka Source重新连接

    我创建一个Source使用 Reactive Kafka 的消费者记录如下 val settings ConsumerSettings system keyDeserializer valueDeserializer withBootstr
  • MYSQL:转换时间戳 --> 时间

    我有这个 MYSQL 表 ID TIMESTAMP TIME 1 2010 05 29 01 17 35 1 2010 05 29 01 23 42 等等 现在我需要将 TIMESTAMP 值复制到 TIME 行中 新的 TIME 行是通过
  • Android + Google 一键刷新令牌?

    每当我使用 Android 应用程序登录时谷歌一键 我收到一个TOKEN ID有效期约为一个小时 我应该如何获得刷新令牌以避免在该 TOKEN 时每小时登录expires 我查了官方文档 但没有关于刷新令牌的内容 这是否意味着每次发生这种情
  • 如何修复在python中接收不到kafka消息但在shell中接收到相同消息的问题?

    我想消费来自 kafka 主题的消息 我正在使用 debezium 它记录 mongodb 更改并将它们放入 kafka 队列中 我可以使用 python 代码连接到 kafka 列出 kafka 主题 虽然 当我想使用消息时 它都是空白的
  • 如何从 Typescript 中的函数获取参数类型[重复]

    这个问题在这里已经有答案了 我可能错过了文档中的某些内容 但我无法在打字稿中找到任何方法来获取函数中参数的类型 也就是说 我有一个函数 function test a string b number console log a consol
  • ORACLE IIF 声明

    我在编写 IIF 语句 表和下面给出的语句时遇到错误 陈述 SELECT IIF EMP ID 1 True False from Employee Table CREATE TABLE SCOTT EMPLOYEE EMP ID INTE
  • 自动 jquery 样式表切换器

    我见过许多 css 切换器 它们放置一个按钮 允许用户更改样式以适合他们的口味 我正在寻找尚未找到的类似解决方案 这是最接近的 http net tutsplus com demos 03 jQueryStyleSwitcher demo
  • 在使用 Base 的 OCaml 中,如何构造一个包含“int * int”类型元素的集合?

    在 F 中 我只需执行以下操作 gt let x Set empty val x Set lt a gt when a comparison gt Set add 2 3 x val it Set