2010-08-05 95 views
5

我有一個名爲cube的類型,它表示一個物理多維數據集。我寫了一些需要一個立方體的代碼,並生成了立方體所有可能方向的列表。有沒有更好的方法來計算F#中所有可能的方向?

我已經使用了下列術語,假設立方體坐在我眼前。

多維數據集的面孔:

  1. 頂面天花板。
  2. 底部朝向桌子。
  3. 正面朝向遠離我。
  4. 背對着我。
  5. 左側面向左側的牆。
  6. 右對着我右邊的牆。

對於軸立方體可圍繞旋轉:從表

  1. 正常軸線延伸到天花板。
  2. 縱軸從我身體延伸到我面前的牆壁。
  3. 橫軸從左壁延伸到右壁。

雖然6個面中的每一面都朝下,但立方體可以4種不同方式(0,90,180和270度)圍繞其法線軸旋轉。這導致24種可能的方向。

我已經開始與立方體型(請原諒S/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 

然後我接着寫它提供的功能getOrientations一個立方體模塊。

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] 

這個模塊只是提供了三種可能的90度旋轉,即取一個立方體通過每一個可能的方向旋轉的名單,併產生給出一個多維數據集的所有方向的功能。

如果我做的:

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

我得到:

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 

此我想要做什麼。但是Cube模塊被這個龐大的操作列表所佔據。

有沒有更好的方法來做到這一點,可能更少的操作或完全不同的方法?

+0

作爲參考,骰子這幾天有一個特定的屬性:每邊和其相反的一面總計爲7. – cHao 2010-08-05 11:42:12

+0

@cHao:確實 - 這就是爲什麼我使用術語立方體 - 我想我會更好使用單個字符對於問題而不是整數。 – 2010-08-05 11:47:52

+0

而不是以您的某個功能的名稱使用「Dice」。 :) – cHao 2010-08-05 11:49:57

回答

2

首先:考慮如果縱向旋轉立方體,則執行其他操作,操作順序更規則。 (它變成[長,正常,正常,正常]時間6)。你可以想象將其壓縮成4個操作列表,特別是在一秒鐘內。更重要的是,經過3次這個列表的迭代後,你最終會得到與你一起開始的同一個立方體。

現在,考慮到在旋轉時看到的每個方向,還有一個「相反」的方向。 (IE:對於每個方向(U,D,L,R,F,B),都有相應的方向(D,U,L,R,B,F)(圖片你和外層空間的朋友,相對於另一個,以及你們每個人在立方體的相對側上)。在前12個操作([長左,正常,正常,正常]時間3)中的每一個之後,三邊結束你的「頂部」永遠不會處於「底部」 - 也就是說,你看到的和你的朋友看到​​的東西之間不會有重疊,如果你的朋友也記錄他看到的東西(閱讀:如果你添加了你的視圖和「相反」的觀點在同一時間),你切的轉數的一半

所以(在僞代碼,因爲我不知道F#):

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

最後,你應該有全部24個方向。

+0

我不認爲這是非常正確的 - 在我看來,你所描述的相反方向是反射,而不是旋轉。 – kvb 2010-08-05 17:28:32

+0

你知道,我認爲你是對的。左側和右側不應該切換。編輯。 – cHao 2010-08-05 19:26:21

+0

+1:我喜歡這樣的想法,即不需要一個接一個地依次捕獲方位。我可以做的(類似於你所說的)將計算12個方向,然後對所有這些方向應用最終的「翻轉」來完成該組。我將其視爲公認的解決方案,因爲這是一種我沒有考慮過的方法,我認爲它有很大的潛力。 – 2010-08-10 19:38:38

2

這可能是稍微乾淨:

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 

expand功能需要重複/元素對的列表,並將其擴展伸到元素的列表,這使得操作的列表看起來有點清潔劑(以我,至少)。然後,我們可以使用內置的List.scan函數遍歷該列表,並將每個函數依次應用於先前的結果。

+0

+1:我雖然想做這件事,但從來沒有真正嘗試過。另外,List.scan功能比我所使用的更清潔 - 謝謝! – 2010-08-10 19:33:47

相關問題