2012-05-01 46 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返回一個無限列表,我們將只使用它的有限部分,所以我們不會陷入無限循環。