2012-05-01 44 views
1

我只是这一个困惑,它的循环排序的,事情我无法弄清楚如何写一个Haskell。基本上,我已经定义分裂三大功能,浅滩洗牌循环执行的函数在Haskell

split :: [a] -> ([a],[a]) 
split xs = splitAt (length xs `div` 2) xs 

riffle :: [a] -> [a] -> [a] 
riffle xs [] = xs 
riffle [] ys = ys 
riffle (x:xs) (y:ys) = x:y:riffle xs ys 

shuffle :: Int -> [a] -> [a] 
shuffle 0 xs = xs 
shuffle n xs = shuffle (n-1) (riffle a b) 
    where (a, b) = split xs 

基本上分裂只是分裂成两半的列表,浅滩应该是“隔行扫描”两份清单,因此,例如:

riffle [1,2,3] [4,5,6] = [1,4,2,5,3,6] 

而且洗牌是迭代分裂的量,缩分列出项目。现在我需要定义一个函数重复,它输出要重新获得原始列表需要多少次重复的shuffle。该函数的定义是这样的:

repeats :: [Int] -> Int 

我只是坚持为你怎么能在洗牌完成一个循环。我认为这是与列表理解,但我无法得到任何。我还没有尝试lambda表达式,但我不认为这是必要的。顺便说一句,洗牌应该在偶数个项目的列表上完成。有任何想法吗?

回答

12

一种方法是采取懒惰的优势,并利用iterate生成输入的迭代洗牌的无限名单。

> iterate (uncurry riffle . split) "ABCDEF" 
["ABCDEF","ADBECF","AEDCBF","ACEBDF","ABCDEF","ADBECF","AEDCBF","ACEBDF", ...] 

列表中的第一个元素是原单,所以我们放弃与tail,然后使用takeWhile得到那名与原来不同的人。

> takeWhile (/= "ABCDEF") . tail $ iterate (uncurry riffle . split) "ABCDEF" 
["ADBECF","AEDCBF","ACEBDF"] 

现在,你只需要采取列表的length并添加一个获得所需数量的洗牌。

+1

您也可以用@hammar描述的'tail $ iterate'生成列表,并使用['elemIndex'](http://hackage.haskell.org/packages/archive/base/latest/doc/html/ Data-List.html#v:elemIndex)来查找重复的索引。 – dflemstr

5

在许多情况下,您可以使用无限列表而不是“循环”。这是其中之一。

序曲功能“迭代”反复功能(从内存中)

iterate f x = [x, f x, f (f x), f (f (f x)) ....] 

所以,如果你申请“迭代洗牌”的首发名单中,然后你会得到逐步洗牌适用于价值,所以。然后使用takeWhile在列表中找到等于您的起点的第一个条目,然后使用“length”来查找多长时间。解决这个的

3

在Haskell,重复使用递归而不是通过循环最常见的表达。

这通常是通过使用一个内部函数,告诉你如何做迭代完成,然后简单调用合适的参数内部函数。

也许你可以在下面的代码填补空白?

repeats xs = iter 1 (...) where 
    iter n ys = if ys == xs 
     then n 
     else iter (...) (...) 

另一种方法是利用Haskell的懒惰和具有无限名单做,使用高阶函数iterate,其重复应用的功能,初始参数:

repeats xs = (...) $ takeWhile (...) $ iterate (shuffle 1) (...) 

虽然iterate返回一个无限列表,我们将只使用它的有限部分,所以我们不会陷入无限循环。