2016-04-02 24 views
1

上哈斯克尔研究一些“自我施加的功课”的一部分,我做了汉诺塔的经典解决方案:转化汉诺塔运动顺序为配置顺序

doHanoi :: Int -> Int -> Int -> [(Int, Int)] 
doHanoi 0 _ _ = [] 
doHanoi n from to = first ++ [(from, to)] ++ last 
    where 
     using = 3 - from - to; 
     first = doHanoi (n - 1) from using; 
     last = doHanoi (n - 1) using to 

(其中doHanoi n from to using意义GET运动假设磁盘0.. n - 1的asequence是在PEG from,我们需要将它们移动到挂to。)

这给运动的序列,例如

>>> doHanoi 3 0 2 
[(0,2),(0,1),(2,1),(0,2),(1,0),(1,2),(0,2)] 

然后我想看看是否可以将输出转换为一组配置(即最初,所有环都位于左侧挂钩上,然后存在中间配置,最后所有环都位于右侧挂钩上)。然后我可以使用scanl通过编写changeConfig功能

changeConfig :: [[Int]] -> (Int, Int) -> [[Int]] 
changeConfig [e0:e0s, e1s, e2s] (0, 1) = [e0s, e0:e1s, e2s] 
changeConfig [e0:e0s, e1s, e2s] (0, 2) = [e0s, e1s, e0:e2s] 
changeConfig [e0s, e1:e1s, e2s] (1, 0) = [e1:e0s, e1s, e2s] 
changeConfig [e0s, e1:e1s, e2s] (1, 2) = [e0s, e1s, e1:e2s] 
changeConfig [e0s, e1s, e2:e2s] (2, 0) = [e2:e0s, e1s, e2s] 
changeConfig [e0s, e1s, e2:e2s] (2, 1) = [e0s, e2:e1s, e2s] 

做到这一点:

>>> scanl changeConfig [[0.. 2], [], []] (doHanoi 3 0 2 1) 
[[[0,1,2],[],[]],[[1,2],[],[0]],[[2],[1],[0]],[[2],[0,1],[]],[[],[0,1],[2]],[[0],[1],[2]],[[0],[],[1,2]],[[],[],[0,1,2]]] 

虽然这个作品,我觉得我失去了一些东西在changeConfig:这是所有的配置只是一个穷举,在具有某种形式的循环对称性的环境中,碰巧发生了工作,因为存在三个挂钩,并且不能很好地伸缩(以LOC表示)。什么是“Haskellic”的写法?

+0

发现能否请您记录您的功能? 'n'究竟是什么?究竟是“使用”?你在哪里找到这个算法? – dfeuer

+0

@dfeuer对不起,我添加了一个解释。如果有其他问题,请LMK不清楚。谢谢! –

+0

请注意,'using'不必被指定为参数;它完全由'3 - (from + to)'指定。 – chepner

回答

1

感谢chepner和jberryman的热心帮助,这就是我想出的。

寻找运动的功能是不变:

doHanoi :: Int -> Int -> Int -> [(Int, Int)] 
doHanoi 0 _ _ = [] 
doHanoi n from to = first ++ [(from, to)] ++ last 
    where 
     using = 3 - from - to; 
     first = doHanoi (n - 1) from using; 
     last = doHanoi (n - 1) using to 

现在的辅助功能,changePeg es i from to new_e返回输出钉住i假设它包含的元素es,其指数为i,该运动是从fromto,和元素new_e

changePeg :: [Int] -> Int -> Int -> Int -> Int -> [Int] 
changePeg es i from to new_e 
    | i == from = tail es 
    | i == to = new_e: es 
    | otherwise = es 

利用这一点,changeConfig成为

changeConfig :: [[Int]] -> (Int, Int) -> [[Int]] 
changeConfig es (from, to) = new_es where 
    new_e = head $ es !! from; 
    new_es = [changePeg (es !! i) i from to new_e | i <- [0.. 2]] 

和以前一样,该解决方案可以与

>>> scanl changeConfig [[0.. 2], [], []] (doHanoi 3 0 2) 
[[[0,1,2],[],[]],[[1,2],[],[0]],[[2],[1],[0]],[[2],[0,1],[]],[[],[0,1],[2]],[[0],[1],[2]],[[0],[],[1,2]],[[],[],[0,1,2]]]