我有一个旋转列表的功能:
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
是否是解决问题的最有效或最优雅的方法,但是当我输入此内容并且应该可以使用时,我会想到它。
附录: 现在我已经找到了两个解决方案或者提示。我很乐意欢迎任何其他更快或使用不同方法的解决方案。谢谢!
答案 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
的实施以及基于inits
和tails
的实施应该是二次方的长度。名单。但是,基于inits
和tails
的那个应该效率较低,因为它会执行多次二次遍历。虽然请注意,如果您完全评估结果,这些语句仅适用。此外,编译器可能能够改进代码,因此您必须谨慎处理这些语句。
答案 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
来执行此操作进入正确的位置。
或者,您可以使用split
和inits
的组合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 x
或take 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)