2010-08-05 2 views
5

Ich habe einen Typ namens Cube, der einen physischen Würfel darstellt. Ich habe einen Code geschrieben, der einen Würfel nimmt und eine Liste aller möglichen Orientierungen des Würfels erzeugt.Gibt es eine schönere Möglichkeit, alle möglichen Orientierungen eines Würfels in F # zu berechnen?

Ich habe die folgende Terminologie verwendet, vorausgesetzt, der Würfel sitzt auf Augenhöhe vor mir.

Für die Gesichter der Würfel:

  1. Die Oberseiten der Decke.
  2. Der Boden steht dem Tisch gegenüber.
  3. Die Vorderseite ist von mir abgewandt.
  4. Die Rückseite steht mir gegenüber.
  5. Die linke Seite der Wand links von mir.
  6. Das Recht steht zu der Wand rechts von mir.

    erstreckt sich von dem Tisch an der Decke
    1. der normalen Achse:

    für die Achsen kann der Würfel herum gedreht werden.

  7. Die Längsachse erstreckt sich von mir zur Wand vor mir.
  8. Die Querachse erstreckte sich von der linken Wand bis zur rechten Wand.

Während jede der 6 Flächen bleibt nach unten zeigt, kann der Würfel um seine Hochachse 4 verschiedene Möglichkeiten (0, 90, 180 und 270 Grad) gedreht werden. Dies ergibt 24 mögliche Orientierungen.

Ich habe mit dem Cube-Typ gestartet (bitte entschuldigen S/O Syntaxcoloring):

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 

I, die die Funktion getOrientations vorgesehen ging dann auf eine Cube-Modul zu schreiben.

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] 

Dieses Modul liefert nur drei mögliche 90-Grad-Drehungen, eine Liste von Umdrehungen, die einen Würfel durch jede mögliche Ausrichtung nehmen, und eine Funktion, die einen einzigen Würfel gegeben, alle Orientierungen erzeugt.

Wenn ich tun:

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

ich:

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 

Das tut, was ich will. Aber das Cube-Modul wird von dieser riesigen Liste von Operationen übernommen.

Gibt es einen besseren Weg, dies mit vielleicht weniger Operationen oder einem völlig anderen Ansatz zu tun?

+0

Als Referenz haben Würfel in diesen Tagen eine besondere Eigenschaft: jede Seite und ihre gegenüberliegende Seite summieren sich zu 7. – cHao

+0

@cHao: In der Tat - deshalb habe ich den Begriff Würfel verwendet - ich denke, ich wäre mit einzelnen Zeichen besser dran gewesen eher für die Frage als für ganze Zahlen. –

+0

Und nicht "Würfel" im Namen einer Ihrer Funktionen verwenden. :) – cHao

Antwort

2

Für eine Sache: bedenken Sie, dass wenn Sie den Würfel in Längsrichtung drehen, dann tun Sie die anderen Ops, die Reihenfolge der Ops ist regulärer. (Es wird zu [lang, normal, normal, normal] mal 6). Sie könnten dies in einer Liste von 4 Operationen, vor allem in einer Sekunde, vorstellen. Und nach drei Wiederholungen dieser Liste erhalten Sie den gleichen Würfel, mit dem Sie angefangen haben.

Nun bedenken Sie, dass für jede Orientierung, die Sie während der Drehung sehen, auch eine "entgegengesetzte" Ausrichtung vorhanden ist. (IE: für jede Orientierung (U, D, L, R, F, B) gibt es eine entsprechende Orientierung (D, U, L, R, B, F). (Stellen Sie sich und einen Freund im Weltraum vor, jeweils oben -down in Bezug auf den anderen, und jeder von euch auf gegenüberliegenden Seiten des Würfels.) Nach jeder der ersten 12 Operationen ([long-left, normal, normal, normal] mal 3), die drei Seiten, die auf Ihr "Top" wird niemals auf dem "Boden" sein - das heißt, es wird keine Überlappung zwischen dem, was Sie sehen, und dem, was Ihr Freund sieht, wenn Ihr Freund auch merkt, was er sieht (lesen Sie: wenn Sie Ihre Ansicht hinzufügen und der "Gegenteil" Blick zugleich), schneiden Sie die Anzahl der Umdrehungen in der Hälfte

So (in Pseudo-Code, weil ich weiß, F # nicht.):

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

Am Ende sollten Sie alle 24 Orientierungen haben.

+0

Ich glaube nicht, dass das recht ist - es scheint mir, dass die entgegengesetzte Orientierung, die Sie beschreiben, eine Reflektion ist, keine Rotation. – kvb

+0

Wissen Sie, ich denke, Sie haben Recht. Die linke und rechte Seite sollte nicht geschaltet werden. Bearbeitung. – cHao

+0

+1: Ich mag die Idee, dass die Orientierungen nicht nacheinander nacheinander erfasst werden müssen. Was ich tun könnte (ähnlich dem, was Sie gesagt haben), wäre die Berechnung von 12 Orientierungen, und dann wenden Sie einen letzten "Flip" auf alle von denen, um das Set zu vervollständigen. Ich nenne dies die akzeptierte Lösung, weil es ein Ansatz ist, den ich wirklich nicht in Betracht gezogen habe, und ich denke, er hat viel Potenzial. –

2

Dies könnte etwas sauberer sein:

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 

Die expand Funktion nimmt eine Liste von Wiederholungs/Elementpaare und dehnt es sich in eine Liste von Elementen aus, die die Liste der Operationen ein wenig sauberer aussehen lässt (zu ich zumindest). Dann können wir die integrierte List.scan-Funktion verwenden, um diese Liste zu überspringen und jede Funktion nacheinander auf das vorherige Ergebnis anzuwenden.

+0

+1: Ich hätte etwas dagegen unternommen, aber nie wirklich versucht. Auch die List.scan-Funktion ist viel sauberer als das, was ich hatte - danke! –

Verwandte Themen