如何定义生成给定列表的所有旋转的旋转函数?
例如:旋转[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
但我不知道如何生成这些新数组并将它们附加在一起。
答案 0 :(得分:23)
计算列表所有旋转的另一种方法是使用预定义函数tails
和inits
。函数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
个轮值,因此我们take
是n
的第一个{{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)
我更喜欢以下解决方案,使用内置函数cycle
和tails
:
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)