如何定义旋转功能

时间:2011-10-03 06:27:35

标签: haskell

如何定义生成给定列表的所有旋转的旋转函数?

例如:旋转[1,2,3,4] =[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]

我写了一个可以重新排列顺序的移位功能

 shift ::[Int]->[Int]

 shift x=tail ++ take 1 x

但我不知道如何生成这些新数组并将它们附加在一起。

8 个答案:

答案 0 :(得分:23)

计算列表所有旋转的另一种方法是使用预定义函数tailsinits。函数tails生成列表中所有最终段的列表,而inits生成所有初始段的列表。例如,

tails [1,2,3] = [[1,2,3], [2,3], [3],   []]

inits [1,2,3] = [[],      [1],   [1,2], [1,2,3]]

也就是说,如果我们按照缩进的指示逐点连接这些列表,我们就会得到所有的旋转。我们只得到原始列表两次,即一次通过在原始列表的末尾附加空的初始段,并且通过将空的最终段附加到原始列表的前面一次。因此,我们使用函数init删除将zipWith应用于列表的尾部和内部的结果的最后一个元素。函数zipWith将其第一个参数逐点应用于提供的列表。

allRotations :: [a] -> [[a]]
allRotations l = init (zipWith (++) (tails l) (inits l))

此解决方案优于其他解决方案,因为它不使用length。函数length非常严格,因为它在完全评估其参数的列表结构之前不会产生结果。例如,如果我们评估应用程序

allRotations [1..]

也就是说,我们计算无限自然数列表的所有旋转,ghci愉快地开始打印无限列表作为第一个结果。相反,基于此处建议的length的实现不会终止,因为它会计算无限列表的长度。

答案 1 :(得分:5)

shift (x:xs)  =  xs ++ [x]
rotates xs    =  take (length xs) $ iterate shift xs

iterate f x返回流(“无限列表”)[x, f x, f (f x), ...]n - 元素列表有n个轮值,因此我们taken的第一个{{1}}。

答案 2 :(得分:3)

以下

shift :: [a] -> Int -> [a]
shift l n = drop n l  ++ take n l

allRotations :: [a] -> [[a]]
allRotations l = [ shift l i | i <- [0 .. (length l) -1]]

产量

> ghci
Prelude> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> allRotations [1,2,3,4]
[[1,2,3,4],[2,3,4,1],[3,4,1,2],[4,1,2,3]]

正如您所料。

我认为这是相当可读的,虽然不是特别有效(没有先前轮班的记忆)。


如果你关心效率,那么

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

allRotations :: [a] -> [[a]]
allRotations l = take (length l) (iterate shift l)

将允许您重复使用先前班次的结果,并避免重新计算它们。

请注意iterate会返回一个无限列表,由于延迟评估,我们只会将其评估到列表中length l


请注意,在第一部分中,我已经扩展了你的shift函数以询问要移动多少,然后我对allRotations进行了列表理解。

答案 3 :(得分:1)

到目前为止给出的答案适用于有限列表,但最终会在给出无限列表时出错。 (他们都在列表上调用length。)

shift :: [a] -> [a]
shift xs = drop 1 xs ++ take 1 xs

rotations :: [a] -> [[a]]
rotations xs = zipWith const (iterate shift xs) xs

我的解决方案使用zipWith const代替。 zipWith const foos bars乍一看似乎与foos相同(请回忆const x y = x)。但是,当任一输入列表终止时,从zipWith返回的列表将终止。

因此当xs有限时,返回的列表与xs的长度相同,如我们所愿;当xs无限时,返回的列表将不会被截断,因此将是无限的,再次按我们的意愿。

(在你的特定应用程序中,尝试旋转无限列表可能没有意义。另一方面,它可能。我只提交完整性答案。)

答案 4 :(得分:1)

我更喜欢以下解决方案,使用内置函数cycletails

rotations xs = take len $ map (take len) $ tails $ cycle xs where
    len = length xs 

对于您的示例[1,2,3,4],函数cycle生成无限列表[1,2,3,4,1,2,3,4,1,2...]。函数tails从给定列表生成所有可能的尾部,这里[[1,2,3,4,1,2...],[2,3,4,1,2,3...],[3,4,1,2,3,4...],...]。现在我们需要做的就是将“尾部”列表减少到长度4,并将整个列表切割为长度4,这是使用take完成的。引入了别名len以避免多次重新计算length xs

答案 5 :(得分:0)

我认为它会是这样的(我现在没有ghc,所以我无法尝试)

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

rotateHelper xs 0 = []
rotateHelper xs n = xs : (rotateHelper (shift xs) (n - 1))

rotate xs = rotateHelper xs  (length xs)

答案 6 :(得分:0)

myRotate lst = lst : myRotateiter lst lst
  where myRotateiter (x:xs) orig
          |temp == orig = []
          |otherwise = temp : myRotateiter temp  orig               
          where temp = xs ++ [x]

答案 7 :(得分:0)

我建议:

rotate l = l : rotate (drop 1 l ++ take 1 l)

distinctRotations l = take (length l) (rotate l)