2011-03-22 8 views
7

Hola amigos, tengo el siguiente fragmento de código de C++.C++ a F # traducción suave

for (int i=0; i < nObstacles; i++) 
{ 
    int x,y; 
    bool bAlreadyExists; 
    do {   
    x = rand() % nGridWidth; 
    y = rand() % nGridHeight;     
    } while (HasObstacle(x, y)); 
    SetObstacle(x, y, true);  
} 

Puedo traducirlo a F # directamente sin ningún problema.

let R = new System.Random() 
for i=0 to nObstacles do 
     let mutable bGoodToGo = false; 
     let mutable x =0; 
     let mutable y = 0 
     while not bGoodToGo do 
      x <-R.Next(nWidth) 
      y <-R.Next(nHeight) 
      bGoodToGo <- IsEmptyAt x y 
     board.[x,y]<-Obstacle; 

Por supuesto, esto probablemente hace que la mayoría de ustedes se avergüence, ya que esta no es la forma en que F # debía usarse. Este código tiene algunos conceptos "unkosher" para F #, como bucles do-while y datos mutables.

Pero lo que me gustaría ver es una traducción "correcta" de F # con datos inmutables, y algún tipo de equivalente de do-while.

Respuesta

4

Aquí está mi intento:

Seq.initInfinite (fun _ -> rnd.Next(width), rnd.Next(height)) 
|> Seq.filter (fun (x, y) -> IsEmptyAt x y) 
|> Seq.distinct 
|> Seq.take nObstacles 
|> Seq.iter (fun (x, y) -> board.[x,y] <- Obstacle) 

puede quitar el Seq.filter si la junta está vacío al principio. Al igual que en la solución de Tomas, genera una secuencia infinita de posiciones. Luego, elimina las posiciones malas y duplicadas. Finalmente, actualiza la placa con los primeros elementos nObstacles.

5

Como primer paso, puede ver cómo simplificar el bucle while dentro del lazo for. Una opción es usar Seq.initInfinite para generar una secuencia que le dará cualquier cantidad de coordenadas X, Y aleatorias. Luego puede usar Seq.find para encontrar el primero que se refiere a un campo de tablero vacío.

También ha cambiado isEmpty tomar una tupla (por lo que puede pasar como argumento para Seq.find usando la aplicación de función parcial) y me cambiaron algunos nombres a seguir # estilo más estándar F (por lo general, no se usará la notación de nombres húngaro):

let isEmpty (x, y) = board.[x,y] = -1 

let rnd = new System.Random() 
for i = 0 to obstacleCount do 
    let x, y = 
    // Generate infinite sequence of random X Y coordinates 
    Seq.initInfinite (fun _ -> rnd.Next(width), rnd.Next(height)) 
    // Find first coordinate that refers to empty field 
    |> Seq.find isEmpty 
    // We still have mutation here 
    board.[x,y] <- Obstacle 

creo que esta es una solución funcional muy elegante. Puede ser un poco más lento que la solución imperativa, pero el punto es que el estilo funcional hace que sea más fácil escribir & cambiar la implementación una vez que la aprende (siempre puede usar el estilo imperativo como optimización).

Para evitar todo estado mutable, primero deberá generar ubicaciones para obstáculos y luego inicializar la matriz. Por ejemplo, podría agregar recursivamente nuevas coordenadas a un conjunto hasta que tenga la longitud requerida. A continuación, puede generar matriz mediante Array2D.init:

let rec generateObstacles obstacles = 
    if Set.count obstacles = obstacleCount then obstacles 
    else 
    // Try generating new coordinate and add it to the set 
    // (if it is already included, this doesn't do anything) 
    obstacles 
    |> Set.add (rnd.Next(width), rnd.Next(height)) 
    |> generateObstacles 

let obstacles = generateObstacles Set.empty 
Array2D.init width height (fun x y -> 
    if obstacles.Contains(x, y) then Obstacle else Empty) 

esto no es realmente más corto y va a ser un poco más lento, por lo que me pega a la primera solución. Sin embargo, es un buen ejercicio que muestra la recursividad y conjuntos ...

+0

Creo que su primera solución es interesante. Me hace pensar "fuera de la caja" Probablemente no haya necesidad de inmutabilidad al 100% aquí, ya que el tipo de junta TIENE que ser mutable de todos modos, solo esperaba evitar la mutabilidad de (x, y) – user627943