7

すべての構築が、不変条件を保持できるモジュール メンバーを通過するように型を定義したいと考えていますが、パターン マッチングのための分解は可能です。

私はちょうどOCamlを学んでいますが、以下は、左が厳密に右よりも小さくなければならないという不変条件を持つintペアに対してほとんど機能します

module Range : sig
  type t = private { left:int; right:int }
  exception InvalidRange of (int*int)
  val make : int -> int -> t
end = struct
  type t = { left:int; right:int }
  exception InvalidRange of (int*int)
  let make left right = if left < right
    then { left; right }
    else raise (InvalidRange (left, right))
end

このように機能する

# let p = Range.make 1 2;;
val p : Range.t = {Range.left = 1; Range.right = 2}
# let q = Range.make 2 1;;
Exception: Range.InvalidRange (2, 1).

そして、破壊はファッションの後に機能します

# let {Range.left=x; Range.right=y} = p;;
val x : int = 1
val y : int = 2

構築中に失敗する

# let badp = {Range.left = 2; Range.right = 1};;
  let badp = {Range.left = 2; Range.right = 1};;
Error: Cannot create values of the private type Range.t
# open Range;;
# let badp = {left = 2; right=1};;
  let badp = {left = 2; right=1};;
Error: Cannot create values of the private type Range.t

しかし、私が本当にやりたいのは、タプルを分解するという構文上の利便性を実現することです。以下は機能しません。

module Range : sig
  type t = private int*int
  exception InvalidRange of (int*int)
  val make : int -> int -> t
end = struct
  type t = int*int
  exception InvalidRange of (int*int)
  let make left right = if left < right
    then (left, right)
    else raise (InvalidRange (left, right))
end

しかし、タプルパターンを使用してそれを分解することはできません:

# let r = Range.make 1 2 ;;
val r : Range.t = (1, 2)
# let (a, b) = r;;
  let (a, b) = r;;
Error: This expression has type Range.t
       but an expression was expected of type 'a * 'b

タイプをに変更することもできますが、type t = R of (int * int)これらは可能な限り軽量でメモリ的にする必要があります。何か案は?

4

5 に答える 5

9

マニュアルで説明されているように、明示的な強制が必要です。

# let (a, b) = (r :> int*int);;
val a : int = 1
val b : int = 2
于 2012-05-22T21:01:02.940 に答える
4

これを行う簡単な方法は、to_tuple関数を追加してツアータイプを抽象化することです。

module Range : sig
  type t
  exception InvalidRange of (int*int)
  val make : int -> int -> t
  val to_tuple : t -> (int * int)
end = struct
  type t = { left:int; right:int }
  exception InvalidRange of (int*int)

  let make left right = if left < right
    then { left; right }
    else raise (InvalidRange (left, right))

  let to_tuple t = t.left, t.right

end

その後、あなたはすることができます

let (a, b) = to_tuple range
于 2012-05-23T05:37:56.303 に答える
3

のソリューションはtype t = R of (int * int)メモリの点で軽量になり、強制ソリューションよりも構文的に少し軽量になります。OCaml は、単一コンストラクターのデータ型のケースを最適化するので、料金はかかりません。(私はこの主張に関する公式のリファレンスを持っていませんが、Adam Chlipala (OCaml の専門家) がここで言及しています: http://adam.chlipala.net/cpdt/html/Subset.html )。

于 2012-05-31T20:57:23.853 に答える
2

このテストをObjsizeで実行しました(OCaml 値のサイズを報告します)。

# type fancy = R of int * int;;
type fancy = R of int * int
# Objsize.objsize (R (3, 5));;
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0}
# Objsize.objsize (3,5);;
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0}

これらの値を信じている場合 (私は信じています)、タプルの代わりに独自の単一コンストラクター型を使用することによるサイズのペナルティはありません。

于 2012-05-22T21:26:37.957 に答える
1

実際、OCaml ランタイムのエンコーディングは非常に単純です。便利なツールを次に示します https://github.com/bobzhang/caml-inspect

于 2012-06-03T21:15:55.093 に答える