5

私は一般的に次のようなコードを書くことに満足していません:

let load_record_field cursor gets geti gett a = function
  | 0x01 -> let c, s = gets () in (a.a_record_uuid <- s; `More_record c)
  | 0x02 -> let c, s = gets () in (a.a_group <- s; `More_record c)
  | 0x03 -> let c, s = gets () in (a.a_title <- s; `More_record c)
  | 0x04 -> let c, s = gets () in (a.a_username <- s; `More_record c)
  | 0x07 -> let c, t = gett () in (a.a_creation_time <- t; `More_record c)
  .
  .
  .
  | 0xFF -> `End_of_record cursor

ボイラープレートを最小化しましたが、それを完全に排除できるOCamlの魔法があるかどうか疑問に思いました。

4

3 に答える 3

2

これは非常に単純です。クロージャーを使用して設定を行い、ボイラープレートを抽象化する関数を記述します。

let load_record_field cursor gets geti gett a x =
  let frob get set =
     let (c,s) = get () in
     set s; `More_record c
  in
  function
  | 0x01 -> frob gets (fun s -> a.a_record_uuid <- s)
  | 0x02 -> frob gets (fun s -> a.a_group <- s)
  | 0x03 -> frob gett (fun s -> a.a_title <- s)
  ...

等々。

Jane Streetのfieldslibのようなマクロパッケージを使用すると、これをさらに改善できます。これにより、自動生成されたセッターとゲッターとともに、ファーストクラスのフィールドが生成されます。これは、毎回手作業でクロージャーを作成する必要がないことを意味します。

于 2010-02-27T13:50:39.030 に答える
1

理論的には、最短で逃げることができます。

frobnicate (function 
| 0x01 -> gets , a_record_uuid 
| 0x02 -> gets , a_group 
  ...
)

もちろん、Objective Camlには「メンバーへのポインタ」構造がないため、OCamlに失敗します。したがって、 (少なくとも)fun a s -> a.a_record_uuid <- s代わりに記述する必要があり、2°型システムは存在記号を完全にはサポートしていません。a_record_uuid関数の戻り型が期待できないように、定量化:

exists 'a. int -> (unit -> record * 'a) * ('a -> record -> unit)

十分な頻度でそれを行う場合は、レコードに値を設定するための名前付き関数を使用することで、1°を解決できると思います。

type complex = { re : int ; im : int }
let re r c = { c with re = r }
let im r c = { c with im = i }

それは少し非正統的だと思いますが、私はほとんどの機能的な状況でそれらを使用する傾向があるので、通常は後で報われます。命令型で同等のものを作成することも、関数のオーバーヘッドを受け入れることもできます(約20文字しか追加されません)。

または2°として、関数内の存在記号を非表示にすることで解決できます。

let t e read write = let c, x = read () in write x e ; `More_record c

これにより、次のようになります。

let t = t a in
match 
  | 0x01 -> t gets a_record_uuid 
  | 0x02 -> t gets a_title
  ...

CamlP4が割り当て機能のためにある種の砂糖をサポートしていても驚かないでしょう。それまでの間、可変フィールドの代わりに参照を使用する場合は、これを短くすることができます(参照はファーストクラスの値であるため、フィールドはそうではありません)。

let t read reference = let c, x = read () in reference := x ; `More_record c

match 
  | 0x01 -> t gets a.a_record_uuid
  ...
于 2010-02-26T15:35:54.800 に答える
0

私は一般的にこのようなコードを書くことに満足していません

あなたが私に尋ねれば、良い味の兆候:-)


私は魔法を知りませんが、ボイラープレートを分割するのが最善の方法だと思います。

  1. 可変フィールドごとに1つの定型セッター機能。さまざまな状況で役立つ場合があります。

  2. 整数コードを「このフィールドをどうするか」にマップする1つのデータ構造

関数の代わりにテーブルを使用してレコードスキャナーを実装できます。示唆に富む例を以下に示します。との違いは、ここgetsgettは本当のキッカーです。以下では、

  • sf「文字列フィールド」の略
  • tf「タイムフィールド」の略
  • eor「レコードの終わり」の略

私は自分の例に合わせてtabulate作り上げました。lookup効率的なデータ構造を使用してください。

let sf set a c =     let c, s = gets() in (set a s; `More_record c)
let tf set a c =     let c, s = gett() in (set a t; `More_record c)
let eor    a c =     `End_of_record c

let fields = tabulate
  [ 0x01, sf a_record_uuid
  ; 0x02, sf a_group
  ; ...
  ; 0x07, tf a_creation_time
  ; ...
  ]

let load_record_field cursor gets geti gett a code = lookup fields code cursor a
于 2010-02-27T05:05:28.903 に答える