将列表拆分为多个部分,然后将这些部分混洗:Haskell

时间:2018-02-07 18:55:05

标签: list haskell recursion

问题是:定义一个需要a的函数shuffle :: Int -> [a] -> [a] 自然数n和偶数长度列表,然后拆分然后将列表重复n次。例如,shuffle 2 [1,2,3,4,5,6] = [1,5,4,3,2,6]。我有一个相应的功能浅滩,但我不知道如何拆分列表。

我的浅滩功能是:

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

我开始洗牌,我想,这就是我所拥有的:

shuffle :: Int -> [a] -> [a]
shuffle [] = []
shuffle a xs = (length xs) 'div' a

我试图将一个清单划分为指定的部分" a"。我是Haskell的新手,我仍然不确定这一切是如何运作的:所有的帮助都表示赞赏。

2 个答案:

答案 0 :(得分:5)

要将列表分成两半,按顺序将两半分开,你通常应该使用乌龟和野兔算法。

-- split2 xs = splitAt (length xs `quot` 2) xs
split2 :: [a] -> ([a], [a])
split2 xs0 = go xs0 xs0
  where
    go (_:_:hs) (t:ts) =
      let (front, rear) = go hs ts
      in (t:front, rear)
    go _ ts = ([], ts)

这通常比计算列表的长度并除以2更有效。即使列表是无限的,它也会起作用。

但是,在这种情况下,你不需要懒惰的版本;你马上要立即使用下半场。因此,如果您愿意,可以使用更多Lisp / Scheme / ML风格的版本:

split2' :: [a] -> ([a], [a])
split2' xs0 = go [] xs0 xs0
  where
    go front (_:_:hs) (t:ts) = go (t:front) hs ts
    go front _ ts = (reverse front, ts)

我不确定哪个会更快,但split2'不太容易受到编译器转换引入的空间泄漏的影响。

答案 1 :(得分:1)

不是一个好的洗牌,你的第一个和最后一个元素永远不会移动。

这样的事情?

> let shuffle 0 x = x
      shuffle n x = shuffle (n-1) $ uncurry riffle $ splitAt (length x `div` 2) x

或使用iterate