哈斯克尔:解决河内的塔楼

时间:2012-10-06 13:40:23

标签: haskell towers-of-hanoi

下面的代码解决了hanoi使用预定义函数moveLOD,swapLOI和swapLID返回移动列表。

MoveLOD:将三张光盘从第一个位置移动到三元组第三个位置的第三个位置。另外,一个包含有关移动信息的字符串正在堆叠在字符串列表上。

type Pin = (Char, Int)        -- Represents a rod, named for a character and the number of disks in it.
type Plate = (Pin, Pin, Pin)  -- Represents the configuration of the three rods.(Origin,Intermediate,Destination
type Log = (Plate, [String])  -- Represents a state formed by the configuration of rods and a list of strings that will record the movements made by the algorithm.


moveLOD :: Log -> Log
moveLOD (((o,n), i ,(d,k)),s) = (((o,n-1), i ,(d,k+1)), (o:" -> " ++ [d]):s)

-- swapLOI: Change the positions of the origin rods and intermediate rods.
swapLOI:: Log->Log
swapLOI ((o,i,d),s) = ((i,o,d),s) 

-- swapoLID : Change the positions of the intermediate rods and destination rods.
swapLID:: Log->Log
swapLID ((o,i,d),s) = ((o,d,i),s)

hanoi :: Log -> Log
hanoi:: Int->Log->[String]
hanoi 1 log = transformaLista(moveLOD log)
hanoi n log = hanoi (n-1) (swapLID log) ++ hanoi 1 log ++ hanoi (n-1) (swapLOI(log))

changeToList::Log->[String]
changeToList(p,s) = s

callHanoi:: Int->[String]
callHanoi n = hanoi n ((('O',n),('I',0),('D',0)),[])

2 个答案:

答案 0 :(得分:3)

hanoi :: Log -> Log
hanoi ((('o',0),i,d),s) = ((('o',0),('i',0),('d',0)), [])
hanoi ((('o',1),i,d),s) = moveLOD((('o',1),i,d),s)
hanoi ((('o',n),i,d),s)= hanoi(swapLOI(hanoi(swapLOI(swapLID(moveLOD(swapLID((('o',n),i,d),s)))))))

仅定义Char的{​​{1}}中PinPlate的{​​{1}}的参数函数,还需要提供角色何时为某事物的方程式其他

当接收到与没有定义方程的任何模式匹配的参数时,引发“非详尽模式”错误。解决这个问题的唯一方法是为其余模式提供方程式。

在修改后的代码中,首先,您对原始引脚为空的情况的处理是不正确的,

'o'

表示无论何种hanoi (((o,0),i,d),s) = ((('o',0),('i',0),('d',0)),[]) d,无论何时应用此情况,结果都是相同的。当ihanoi调用且参数大于2时,原点极点会变空,并且从上面开始,调用链中只有chamahanoi和{{1}那个不变的结果冒出来了。您得到hanoi的正确结果(swapLOI由第二个等式直接求解),因为递归调用n == 2然后两者在原点上只有一个磁盘。

那个案子应该是

n == 1

这仍然不会产生正确的结果(错误的移动序列),因为一般情况下的递归是错误的。

  • 将顶部磁盘移动到中间引脚(hanoi);
  • 然后将剩余的磁盘移动到目标(hanoi (((o,0),i,d),s) = (((o,0),i,d),s) ),但这是不允许的,因为最小的磁盘位于中间引脚上,因此不能放置其他磁盘;
  • 最后,使用(现在为空)原点引脚将磁盘从中间引脚移动到目标位置。

你应该

  • swapLID . moveLOD . swapLID个磁盘从原点移动到中间引脚
  • 然后将底部(最大)磁盘移动到目标
  • 最后,将hanoi磁盘从中间位置移动到目标位置。

如果没有额外的参数来跟踪要移动的磁盘数量,我看不到一种简单的方法。考虑一个四磁盘游戏。首先,将前三个磁盘移动到中间引脚,然后将底部磁盘移动到目标引脚。现在的任务是将三个磁盘从中间引脚移动到目标引脚,使用原始引脚作为帮助。

正确的方法是序列

  1. n-1n-1
  2. i -> d([],[1,2,3],[4]) -> ([],[2,3],[1,4])
  3. i -> o([],[2,3],[1,4]) -> ([2],[3],[1,4])
  4. d -> o([2],[3],[1,4]) -> ([1,2],[3],[4])
  5. i -> d([1,2],[3],[4]) -> ([1,2],[],[3,4])
  6. o -> i([1,2],[],[3,4]) -> ([2],[1],[3,4])
  7. o -> d([2],[1],[3,4]) -> ([],[1],[2,3,4])
  8. 在步骤2之后,原始目标引脚成为将磁盘(一个)移动到i -> d的引脚,但在这种情况下,最低的引脚不会被移动。如果唯一的信息是每个引脚上有多少个磁盘,以及磁盘的移动位置,怎么能实现呢?

    如果您将([],[1],[2,3,4]) -> ([],[],[1,2,3,4])的类型更改为

    o

    并将其命名为

    hanoi

    很容易实现。

    如果您不想这样做,或者不允许这样做,您可以跟踪每个引脚的大小,只将磁盘移动到较大的磁盘上,或者您可以偷偷地删除并添加适当的磁盘如果没有正确的解释,就很难区分作弊。

答案 1 :(得分:3)

如果它对任何人有帮助,这里是另一个河内算法塔:

hanoi 0 _ _ _ = []
hanoi n a b c = hanoi (n-1) a c b ++ [(a,b)] ++ hanoi (n-1) c b a

例如,hanoi 2“a”“b”“c”== [(“a”,“c”),(“a”,“b”),(“c”,“b”)]