2011-03-20 7 views
5

En Javascript hay un patrón llamado patrón ruso de muñeca (esto también se puede llamar 'disparo único'). Básicamente, es una función que se reemplaza por otra en algún momento.¿Cómo implementar el patrón de muñeca rusa en Ocaml?

ejemplo simple:

var func = function(){ 
    func = function(){ console.log("subsequent calls call this...");}; 
    console.log("first call"); 
} 

Así que la primera vez que llame func que va de salida "primera llamada" y el siguiente (y los tiempos posteriores) es de impresión "llamadas posteriores llaman a esto ...". (Esto también sería fácil de hacer en Scheme, por ejemplo)

He estado desconcertando sobre cómo hacer esto en Ocaml?

Editar: Una solución que he llegado con:

let rec func = ref(fun() -> func := (fun() -> Printf.printf("subsequent..\n"));Printf.printf("First..\n"));; 

llama como: func() ;;

Curiosamente, si no incluyo el 'rec' en la definición, nunca llama a la función siguiente ... Siempre imprime 'Primero ...'.

+4

Eso es ... simplemente incorrecto. – phooji

+2

tenga en cuenta que las soluciones OCaml propuestas a continuación son todas "más claras" que su ejemplo JavaScript, ya que encapsulan la mutabilidad de "func": después de la primera llamada, la función se cambia para siempre y nadie tiene acceso a una referencia para cambiarla espalda. Esto se hace haciendo que la referencia "f" sea local a la llamada "muñeca". Por supuesto, si desea que la referencia esté disponible para su modificación, también sería posible. – gasche

Respuesta

9

Es bastante sencillo, pero debe usar efectos secundarios. Aquí hay una función que toma dos thunks como argumentos, y devuelve un nuevo thunk que llama al primer thunk la primera vez, y el segundo thunk cada dos veces.

let doll f1 f2 = 
    let f = ref f1 in 
    (fun() -> 
     let g = !f in 
     f := f2; 
     g()) 

Esto no es muy óptimo, porque seguiremos sobrescribiendo la referencia con el mismo valor una y otra vez.

Aquí hay una versión ligeramente mejor, que usa una definición recursiva.

let doll f1 f2 = 
    let rec f = ref (fun() -> f := f2;f1()) in 
    (fun() -> !f()) 

Así pues, ahora, que obtendrá esto:

# let f = doll (fun() -> 1) (fun() -> 2);; 
val f : unit -> int = <fun> 
# f();; 
- : int = 1 
# f();; 
- : int = 2 
10

yzzlr respuesta es muy buena, pero dos observaciones:

Obliga a la entrada de las funciones a ser de tipo de unidad . Se puede utilizar una versión polimórfica:

let doll f1 f2 = 
    let rec f = ref (fun x -> f := f2; f1 x) in 
    (fun x -> !f x);; 

se puede prescindir de la recursividad peluda:

let doll f1 f2 = 
    let f = ref f1 in 
    f := (fun x -> f := f2; f1 x); 
    (fun x -> !f x);; 

(recursividad Sustitución de la mutación es un truco común, sino que en realidad se puede utilizar para definir puntos de fijación sin utilizar " rec ")

Cuestiones relacionadas