Pregunta

En general estoy satisfecho con la escritura de código como el siguiente:

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

he minimizado el repetitivo, pero me preguntaba si había alguna magia OCaml que me permitiera eliminarlo por completo.

¿Fue útil?

Solución

Esto es muy simple: sólo tiene que utilizar un cierre de hacer el ajuste, y escribir una función para abstraer el repetitivo

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)
  ...

y así sucesivamente.

Puede hacer esto aún mejor si se utiliza un paquete de macros como Jane fieldslib de calle. Eso genera campos de primera clase, junto con generada automáticamente incubadoras y getters. Esto significaría que se no tendría que construir el cierre cada vez con la mano.

Otros consejos

El más corto que podría salirse con la suya en teoría es:

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

Por supuesto, se le frustrado por OCaml porque: 1 ° No hay "puntero a miembro de" construcciones en Objective Caml, por lo que tendría que fun a s -> a.a_record_uuid <- s escritura en lugar de a_record_uuid (por lo menos) ° el sistema de tipos y 2 no es totalmente compatible cuantificación existencial, de manera que el tipo de retorno de la función no puede ser el esperado:

  

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

Creo que se puede resolver 1 ° por tener funciones con nombre para establecer valores en un registro, si quieres pasar a hacerlo con la suficiente frecuencia:

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

Es un poco ortodoxo, supongo, pero por lo general vale la pena más adelante porque tiendo a utilizar en la mayoría de situaciones funcionales. Se puede crear el equivalente en estilo imperativo, ya que podría aceptar la sobrecarga de una función (sólo añade alrededor de 20 caracteres).

como o 2 °, que puede ser resuelto por ocultar el cuantificador existencial en una función:

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

Esto le permitiría bajar a:

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

Me no se sorprenda si camlp4 apoya algún tipo de azúcar para las funciones de asignación. Por el momento, si utiliza referencias en lugar de campos variables, se puede acortar esto (porque las referencias son valores de primera clase, los campos no son):

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

match 
  | 0x01 -> t gets a.a_record_uuid
  ...
  

En general estoy satisfecho con la escritura de código como este

Una muestra del buen gusto, si me preguntas: -)


No conozco ningún magia, pero creo que el mejor camino es dividir el texto modelo:

  1. Una de las funciones repetitivo setter para cada campo mutable. Puede ser útil en diferentes contextos.

  2. Una estructura de datos para trazar un mapa entero de códigos de "qué hacer con este campo"

Se puede aplicar el escáner registro usando una tabla en lugar de una función. Un ejemplo sugerente aparece a continuación. La diferencia entre gets y gett es un golpeador verdadero aquí. En lo que sigue,

  • sf significa "campo de cadena"
  • tf significa "campo de tiempo"
  • eor significa "fin del registro"

he tomado una tabulate y lookup para satisfacer mi ejemplo; estructura de uso de cualquier dato que es eficiente.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top