Haskell中的所有列表轮换

时间:2012-08-08 18:08:38

标签: list haskell functional-programming

我有一个旋转列表的功能:

rotate :: [a] -> [a]
rotate [] = []
rotate (x:xs) = xs ++ [x]

现在我想要一个函数,它给出一个包含有限列表的每个可能旋转的列表:

rotateAll :: [a] -> [[a]]

在命令式语言中,我会做(像伪代码)

for i = 1 to length of list
  append list to rotateList
  list = rotate(list)

当然,强制性思考可能无法帮助我找到解决这个问题的功能性解决方案。我正在寻找一些关于如何解决这个问题的提示。

其他想法:

要解决这个问题,我有两个问题需要解决。首先,我需要重复旋转列表并将每个结果收集到一个列表中。所以第一个解决方案需要做类似

的事情
rotateAll xs = [xs (rotate xs) (rotate (rotate xs)) (rotate (rotate (rotate xs))) ...]

当然我不知道要做多少次。我会满意地无限地执行此操作,然后使用take (length xs)来获取我想要的有限数量的列表。这实际上证明了第二个问题:确定何时停止。我不知道使用take是否是解决问题的最有效或最优雅的方法,但是当我输入此内容并且应该可以使用时,我会想到它。

附录: 现在我已经找到了两个解决方案或者提示。我很乐意欢迎任何其他更快或使用不同方法的解决方案。谢谢!

11 个答案:

答案 0 :(得分:7)

使用Data.List中的预定义功能!您可以使用四个函数调用,无递归和无rotate函数来获取所有旋转的列表。

您要求不要在此处发布完整的解决方案。对于那些想要查看它的人,http://pastebin.com/atGiw1ig会出现一个完整的解决方案(一行代码)。

答案 1 :(得分:3)

除了iterate之外,您还可以编写一个生成n个旋转列表的函数。情况n = 0只是将输入包装在列表中,而情况n = m + 1会将输入附加到情况m的结果。虽然通常首选使用标准功能,但有时编写自己的解决方案是健康的。

答案 2 :(得分:3)

这是一个完全懒惰的版本,它在旋转列表本身和列表中的每个单独条目中都是如此。关键是,不是预先计算长度,而是将结果中的元素与列表中的元素进行匹配,在输入列表用完时停止。

rotations xs = map (takeSame xs) $ takeSame xs (tails (xs ++ xs)) where
    takeSame [] _ = [] 
    takeSame (_:xs) (y:ys) = y:takeSame xs ys

此外,由于其使用尾部而且仅使用单个连接,因此在某些其他选择中,这比其他选择更好。当然,它也可以正确处理无限列表。

答案 3 :(得分:2)

您可能还想考虑回答同一问题的网站How to define a rotates function

根据评论进行修改:基于rotate的实施以及基于initstails的实施应该是二次方的长度。名单。但是,基于initstails的那个应该效率较低,因为它会执行多次二次遍历。虽然请注意,如果您完全评估结果,这些语句仅适用。此外,编译器可能能够改进代码,因此您必须谨慎处理这些语句。

答案 4 :(得分:2)

rotations (x:xs) = (xs++[x]):(rotations (xs++[x]) ) 

这会产生持续的懒惰旋转 现在只取第一个唯一的,它将等于原始列表的长度

take (length xs) (rotations xs)

答案 5 :(得分:1)

我想出了两个解决方案。首先是一个手工制作的,在我发布我的问题之后来找我:

rotateAll :: [a] -> [[a]]
rotateAll xs = rotateAll' xs (length xs)
    where rotateAll' xs 1 = [xs]
          rotateAll' xs n = xs : rotateAll' (rotate xs) (n - 1)

另一个使用@ Tilo的建议:

rotateAll :: [a] -> [[a]]
rotateAll xs = take (length xs) (iterate rotate xs)

答案 6 :(得分:1)

你也可以递归地生成它们。生成空元素列表或单个元素列表的所有旋转都是微不足道的,并且生成x:xs的旋转是将x插入xs的所有旋转的正确位置的问题。

您可以通过生成要插入的索引(只是列表[1,2,...],假设先前的轮换按此顺序)并使用zipWith插入x来执行此操作进入正确的位置。

或者,您可以使用splitinits的组合tails围绕该位置进行旋转,并使用zipWith将它们粘合在一起。

答案 7 :(得分:1)

  

附录:现在我已经找到了两个解决方案或者提示。   我很乐意欢迎任何其他更快或更快的解决方案   不同的方法。谢谢!

由于没有其他人使用cycle指出我认为我会为有限列表添加此解决方案:

rotations x = let n = length x in map (take n) $ take n (tails (cycle x))

对于无限列表x,轮换只是tails x

评估cycle x是O(n)时间和空间,tails的每个元素是O(1),take n是O(n)时间和空间但是两个take n是嵌套的,因此评估整个结果是O(n ^ 2)时间和空间。

如果它在懒惰地生成下一个旋转之前收集每个旋转,那么该空间理论上是O(n)。

如果你很聪明你消耗了多少,那么你不需要map (take n),只需走cycle xtake n (tails (cycle x))并保留空间O(n)。

答案 8 :(得分:1)

从头开始:

data [] a = [] | a : [a]

end :: a -> [a] -> [a]
end y []       = y : []
end y (x : xs) = x : y `end` xs

-- such that `end x xs  =  xs ++ [x]`

rotating :: [a] -> [[a]]
rotating [] = []
rotating xs = rots xs
   where
      rots xs@(x : xs') = xs : rots (x `end` xs')

-- An infinite list of rotations

rotations :: [a] -> [[a]]
rotations xs = rots xs (rotating xs)
   where
      rots []       _        = []
      rots (_ : xs) (r : rs) = r : rots xs rs

-- All unique rotations, `forall xs.` equivalent to `take
-- (length xs) (rotating xs)`

或者:

{-# LANGUAGE BangPatters #-}

rotate :: [a] -> [a]
rotate []       = []
rotate (x : xs) = x `end` xs
   where
      end y []       = y : []
      end y (x : xs) = x : end y xs

iterate :: Int -> (a -> a) -> a -> [a]
iterate 0 _ _ = []
iterate n f x = case f x of
                   x' -> x' : iterate (n - 1) f x'

length :: [a] -> Int
length xs = len 0 xs
   where
      len !n []       = n
      len !n (_ : xs) = len (n + 1) xs

rotations :: [a] -> [[a]]
rotations xs = iterate (length xs) rotate xs

-- Starting with single rotation

或者,整合:

rotations :: [a] -> [[a]]
rotations [] = [[]]
rotations xs = rots xs xs
   where
      rots []       _            = []
      rots (_ : xc) xs@(x : xs') = xs : rots xc (x `end` xs')

      end y []       = y : []
      end y (x : xs) = x : end y xs

答案 9 :(得分:1)

也许您可以使用Data.List

import Data.List
rotate x=[take (length x) $ drop i $ cycle x | i<-[0..length x-1]]

答案 10 :(得分:0)

我使用以下rotations函数作为permutations algorithm的帮助器。它似乎是所有这里最快的。

rotations :: [a] -> [[a]]
rotations xs = take (length xs) (iterate (\(y:ys) -> ys ++ [y]) xs)