2010-08-05 18 views
5

Tengo un tipo llamado cubo, que representa un cubo físico. He escrito un código que toma un cubo y genera una lista de todas las orientaciones posibles del cubo.¿Existe alguna manera más agradable de calcular todas las orientaciones posibles de un cubo en F #?

He utilizado la siguiente terminología, suponiendo que el cubo está sentado frente a mí a la altura de los ojos.

Para caras del cubo:

  1. La parte superior se enfrenta al techo.
  2. La parte inferior está frente a la mesa.
  3. El frente está lejos de mí.
  4. La parte trasera está hacia mí.
  5. La izquierda se enfrenta a la pared a la izquierda de mí.
  6. La derecha se enfrenta a la pared a la derecha de mí.

para los ejes del cubo se puede girar alrededor de:

  1. Los tramos eje normal de la tabla en el techo.
  2. El eje longitudinal se extiende desde mí hacia la pared frente a mí.
  3. El eje lateral se extendía desde la pared izquierda hasta la pared derecha.

Mientras que cada una de las 6 caras permanece hacia abajo, el cubo se puede girar alrededor de su eje normal de 4 formas diferentes (0, 90, 180 y 270 grados). Esto resulta en 24 orientaciones posibles.

he comenzado con el tipo de cubo (disculpen S/coloreado de sintaxis de O):

type 'a cube(top:'a, bottom:'a, left:'a, right:'a, front:'a, back:'a) = 
    member this.Top = top 
    member this.Bottom = bottom 
    member this.Left = left 
    member this.Right = right 
    member this.Front = front 
    member this.Back = back 
    override this.ToString() = 
     sprintf "Top: %O, Bottom: %O, Left: %O, Right: %O Front: %O, Back: %O" top bottom left right front back 

Entonces fui a escribir un módulo Cubo que proporcionó los getOrientations de función.

module Cube = 
    let rotateNormalRight (c:'a cube) = 
     cube(c.Top, c.Bottom, c.Back, c.Front, c.Left, c.Right) 
    let rotateLongitudinalRight (c:'a cube) = 
     cube(c.Left, c.Right, c.Bottom, c.Top, c.Front, c.Back) 
    let rotateLongitudinalLeft (c:'a cube) = 
     cube(c.Right, c.Left, c.Top, c.Bottom, c.Front, c.Back) 
    let private operations = 
     [ rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight 
      rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight 
      rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalLeft 
      rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalLeft 
      rotateNormalRight; rotateNormalRight; rotateNormalRight; rotateLongitudinalRight 
      rotateNormalRight; rotateNormalRight; rotateNormalRight ] 
    let getOrientations startCube = 
     let rec getCubeInner (ops:('a cube -> 'a cube) list) (cl:'a cube list) = 
      match ops with 
      | [] -> cl 
      | op :: rest -> getCubeInner rest ((cl |> List.hd |> op) :: cl) 
     getCubeInner operations [startCube] 

Este módulo simplemente proporciona tres posibles rotaciones de 90 grados, una lista de las rotaciones que se llevan a un cubo a través de cada orientación posible, y una función que produce todas las orientaciones dadas un solo cubo.

Si hago:

cube(1, 2, 3, 4, 5, 6) 
|> Cube.getOrientations 
|> List.iter (printfn "%O") 

me sale:

Top: 3, Bottom: 4, Left: 1, Right: 2 Front: 6, Back: 5 
Top: 3, Bottom: 4, Left: 6, Right: 5 Front: 2, Back: 1 
Top: 3, Bottom: 4, Left: 2, Right: 1 Front: 5, Back: 6 
Top: 3, Bottom: 4, Left: 5, Right: 6 Front: 1, Back: 2 
Top: 6, Bottom: 5, Left: 3, Right: 4 Front: 1, Back: 2 
Top: 6, Bottom: 5, Left: 1, Right: 2 Front: 4, Back: 3 
Top: 6, Bottom: 5, Left: 4, Right: 3 Front: 2, Back: 1 
Top: 6, Bottom: 5, Left: 2, Right: 1 Front: 3, Back: 4 
Top: 2, Bottom: 1, Left: 5, Right: 6 Front: 3, Back: 4 
Top: 2, Bottom: 1, Left: 3, Right: 4 Front: 6, Back: 5 
Top: 2, Bottom: 1, Left: 6, Right: 5 Front: 4, Back: 3 
Top: 2, Bottom: 1, Left: 4, Right: 3 Front: 5, Back: 6 
Top: 4, Bottom: 3, Left: 1, Right: 2 Front: 5, Back: 6 
Top: 4, Bottom: 3, Left: 5, Right: 6 Front: 2, Back: 1 
Top: 4, Bottom: 3, Left: 2, Right: 1 Front: 6, Back: 5 
Top: 4, Bottom: 3, Left: 6, Right: 5 Front: 1, Back: 2 
Top: 5, Bottom: 6, Left: 4, Right: 3 Front: 1, Back: 2 
Top: 5, Bottom: 6, Left: 1, Right: 2 Front: 3, Back: 4 
Top: 5, Bottom: 6, Left: 3, Right: 4 Front: 2, Back: 1 
Top: 5, Bottom: 6, Left: 2, Right: 1 Front: 4, Back: 3 
Top: 1, Bottom: 2, Left: 5, Right: 6 Front: 4, Back: 3 
Top: 1, Bottom: 2, Left: 4, Right: 3 Front: 6, Back: 5 
Top: 1, Bottom: 2, Left: 6, Right: 5 Front: 3, Back: 4 
Top: 1, Bottom: 2, Left: 3, Right: 4 Front: 5, Back: 6 

Esto hace lo que yo quiero. Pero el módulo Cube está ocupado por esa enorme lista de operaciones.

¿Hay una mejor manera de hacerlo con menos operaciones o con un enfoque completamente diferente?

+0

Como referencia, los dados en estos días tienen una propiedad en particular: cada lado y su lado opuesto suman hasta 7. – cHao

+0

@cHao: De hecho, es por eso que usé el término cubo, supongo que hubiera sido mejor usar caracteres sueltos para la pregunta en lugar de enteros. –

+0

Y no usa "Dados" en nombre de una de sus funciones. :) – cHao

Respuesta

2

Por un lado: considere que si gira el cubo longitudinalmente, luego haga las otras operaciones, la secuencia de operaciones es más regular. (Se convierte en [largo, normal, normal, normal] veces 6). Es concebible que puedas condensar esto en una lista de 4 operaciones, especialmente en un segundo. Y lo que es más, después de 3 iteraciones de esta lista, terminas con el mismo cubo con el que comenzaste.

Ahora, considere que por cada orientación que vea mientras gira, también existe una orientación "opuesta". (IE: para cada orientación (U, D, L, R, F, B), hay una orientación correspondiente (D, U, L, R, B, F). (Imagínate a ti y a un amigo en el espacio exterior, cada uno al revés -abajo en relación con el otro, y cada uno de ustedes en lados opuestos del cubo.) Después de cada una de las primeras 12 operaciones ([largo-izquierda, normal, normal, normal] veces 3), los tres lados que terminan en su "parte superior" nunca va a estar en el "fondo", es decir, no habrá superposición entre lo que ve y lo que su amigo ve. Si su amigo también nota lo que ve (léase: si agrega su vista) y la vista "opuesto" al mismo tiempo), se redujo el número de rotaciones en medio

por lo tanto (en pseudocódigo, porque yo no sé F #):.

ops = [rotateLongLeft, rotateNormalRight, rotateNormalRight, rotateNormalRight] 
for each operation in [ops times 3]: 
    do the operation 
    add the orientation 
    add its opposite 

Al final, debe tener las 24 orientaciones.

+0

No creo que esto sea del todo correcto, me parece que la orientación opuesta que describes es un reflejo, no una rotación. – kvb

+0

Sabes, creo que tienes razón. Los lados izquierdo y derecho no deben ser cambiados. Edición. – cHao

+0

+1: Me gusta la idea de que las orientaciones no necesitan ser capturadas secuencialmente una después de la otra. Lo que podría hacer (similar a lo que dijo) sería calcular 12 orientaciones, y luego aplicar un 'salto' final a todas esas para completar el conjunto. Estoy marcando esto como la solución aceptada porque es un enfoque que realmente no consideré y creo que tiene un gran potencial. –

2

Esto podría ser un poco más limpio:

let rec expand = function 
| [] -> [] 
| (0,o)::l -> expand l 
| (n,o)::l -> o::(expand ((n-1,o)::l)) 

let getOrientations startCube = 
    expand [ 
    3,rotateNormalRight; 1,rotateLongitudinalRight 
    3,rotateNormalRight; 1,rotateLongitudinalRight 
    3,rotateNormalRight; 1,rotateLongitudinalLeft 
    3,rotateNormalRight; 1,rotateLongitudinalLeft 
    3,rotateNormalRight; 1,rotateLongitudinalRight 
    3,rotateNormalRight] 
    |> List.scan (|>) startCube 

La función expand toma una lista de repetición de pares/elemento y se expande hacia fuera en una lista de elementos, lo que hace que la lista de operaciones mirar un poco más limpia (a yo, al menos). Entonces, podemos usar la función incorporada List.scan para revisar esa lista y aplicar cada función al resultado anterior de forma sucesiva.

+0

+1: Pensé en hacer algo así, pero nunca lo intenté. Además, la función List.scan es mucho más limpia que la que tenía, ¡gracias! –

Cuestiones relacionadas