7

I'm currently trying to do some mahjong hand processing in OCaml and straight from the beginning I'm confronted with something that bugs me.

I'll give you examples based on cards because I don't want to confuse anyone with mahjong terminology.

Just like in this part on User-Defined Types from OCaml for the Skeptical, I want to use variant types to describe suits, cards, and everything.

type suit = Club | Diamond |  Heart | Spade
type value = Jack | Queen | King | Ace | Num of int
type card = Card of suit * value | Joker
type hand = card list

And it would be really nice if I could write a smart compare function that would understand ordered variant types.

Ideally I'd write something like that:

type suit = Club < Diamond <  Heart < Spade
type value = Num of int < Jack < Queen < King < Ace
type card = Card of suit * value < Joker
type hand = card list

So that when I do

List.sort Pervasives.compare [Card(Diamond, Num 3); Joker; Card(Spade, Ace); Card(Diamond, Num 2)]

it gives me

[Card(Diamond, Num 2); Card(Diamond, Num 3); Card(Spade, Ace); Joker]

Alas, the ocaml toplevel returns

[Joker; Card(Spade, Ace); Card(Diamond, Num 2); Card(Diamond, Num 3)]

(which is already quite good!)

Basically I want a compare function that would take hints from the type declaration structure.

I've read this article on polymorphic compare and this similar question but I'm not sure I want to depend on compare_val.

Do I really have to write my own compare function? If you recommend me to write one, do you have tips on the way it should be written, especially to reduce the number of cases?

P.S.: I just heard about deriving(Ord) in Haskell... Might be enough for me to take the leap...

4

2 回答 2

8

是的,你必须。但是您可以跳过多态比较与您的需要相匹配的地方。例如,你不需要写你的西装比较。

Haskell 的 deriving(Ord) 和多态比较一样:如果你能按照你脑海中的顺序对构造函数进行排序,那么你就可以导出比较函数。但它更强大,因为您可以编写自动和自定义比较功能。OCaml 的多态比较不能做到这一点。例如,

type t = ...
let compare_t = .... (* custom comparison for t *)
type t2 = A of t | B of t | C of t (* I want to write a comparion for t2 now! *)

如果构造函数 A、B 和 C 的多态比较顺序符合您的需要,则不能将其用于 t2 的比较,因为它无法调用 t 的自定义比较。所以在这种情况下,如果我是你,我会手写 compare_t2。对于您的卡片示例,也可以在 3 分钟内轻松完成。

如果您的数据类型很大,并且手动写下所有比较非常痛苦,您可以使用 CamlP4 和 type_conv 从类型定义中自动生成比较函数,就像 deriving(Ord) 一样。但恐怕还没有提供类似 Ord 的 type_conv 模块。就我个人而言,我从来没有觉得有必要拥有它。对于 P4 学习者来说,这应该是一个很好的练习。

于 2013-06-18T22:13:26.690 回答
2

我迟到了 7 年,但您可以使用ppx_deriving来实现:

type value = Num of int | Jack | Queen | King | Ace [@@deriving ord]
type suit = Club | Diamond | Heart | Spade [@@deriving ord]
type card = Card of suit * value | Joker [@@deriving ord]
type hand = card list

用于ocamlfind ocamlopt -o executable.out -linkpkg -package ppx_deriving.ord file.ml链接包。

于 2020-04-25T14:15:08.700 回答