2010-08-05 64 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

相关问题