我试图从头开始,不使用标准库之外的库。继承我的代码:
permutations :: [a] -> [[a]]
permutations (x:xs) = [x] : permutations' xs
where permutations' (x:xs) = (:) <$> [x] <*> split xs
split l = [[x] | x <- l]
问题是这只产生一个非确定性计算的分支。理想情况下我想要
(:) <$> [x] <*> ((:) <$> [x] <*> ((:) <$> [x] <*> ((:) <$> [x] <*> xs)))
但我找不到干净利落的方法。我想要的结果是这样的:
permutations "abc" -> ["abc", "acb", "bac", "bca", "cab", "cba"]
我该怎么做?
答案 0 :(得分:19)
也许您应该使用现有代码:
import Data.List
permutations [1,2,3,4]
答案 1 :(得分:4)
对于一个简单的实现,不考虑输入中的重复
permutations :: Eq a => [a] -> [[a]]
permutations [] = [[]]
permutations as = do a <- as
let l = delete a as
ls <- permutations l
return $ a : ls
测试:
λ> permutations [1,2,3]
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
λ> permutations "abc"
["abc","acb","bac","bca","cab","cba"]
λ>
答案 2 :(得分:1)
我这样做:
select :: [a] -> [(a,[a])]
select = select' id where
select' _ [] = []
select' acc (a:r) = (a, acc r) : select' (acc . (a:)) r
permutations [] = [[]]
permutations l = do
(a,r1) <- select l
r2 <- permutations r1
return (a: r2)
答案 3 :(得分:1)
我对Haskell比较新,但I had developed a very efficient permutations algorithm for JS。它几乎击败了堆算法,但在JS中,与列表上的惰性Haskell iterate
函数相比,旋转数组的成本更高。因此,与上述所有提供的答案不同,这一点似乎更有效率。
内置Data.List.permutations
仍然比今天快2倍,因为我根本不知道Haskell的性能限制。可能是这里的某个人可以帮我推进这个代码。
所以我有一个辅助函数,它返回所提供列表的所有旋转列表。如
rotations [1,2,3]
会产生[[1,2,3],[2,3,1],[3,1,2]]
因此perms函数是;
rotations :: [a] -> [[a]]
rotations xs = take (length xs) (iterate (\(y:ys) -> ys ++ [y]) xs)
perms :: [a] -> [[a]]
perms [] = [[]]
perms (x:xs) = concatMap (rotations.(x:)) (perms xs)
编辑:所以我一直在考虑如何提高上述代码的效率。确定Haskell中的列表是链接列表,与JavaScript不同,长度不是您可以在O(1)时间访问但O(n)的属性。它是遍历整个该死的列表的函数,基本上计算列表中的所有项目。因此反复使用非常昂贵。这恰好是我们在每次调用rotate函数时通过take (length xs)
指令所做的事情。如果您的输入列表长度为10-11项或更长,我们实际上会调用数百万次。削减它将产生巨大的节省。然后我们不要让它计算相同长度列表的长度,而是让它简单地提供它;
rotations :: Int -> [a] -> [[a]]
rotations len xs = take len (iterate (\(y:ys) -> ys ++ [y]) xs)
美丽。那么,现在我们必须稍微修改我们的perms
函数,如;
perms :: [a] -> [[a]]
perms [] = [[]]
perms il@(x:xs) = concatMap ((rotations len).(x:)) (perms xs)
where len = length il
所以显然il
现已分配给 i 输入 l ist,len
缓存它的长度。现在这很漂亮,非常有趣的是它比默认的Data.List.permutations
运行 更快 。
到目前为止一直很好......但是现在是时候作弊了一点,让它变得更快。我知道的一件事,正如我刚刚证明的那样,在CS中,算法的最佳燃料是缓存。那么我们在这里继续缓存什么呢?我们可以轻松缓存[x]
,[x,y]
和[x,y,z]
等输入的返回值,并保存一些计算。你想要更快的速度..?然后无耻地缓存[w,x,y,z]
。让我们看一下整个新代码;
rotations :: Int -> [a] -> [[a]]
rotations len xs = take len (iterate (\(y:ys) -> ys ++ [y]) xs)
perms :: [a] -> [[a]]
perms [] = [[]]
perms [x] = [[x]]
perms [x,y] = [[x,y],[y,x]]
perms [x,y,z] = [[x,y,z],[y,z,x],[z,x,y],[x,z,y],[z,y,x],[y,x,z]]
perms il@(x:xs) = concatMap ((rotations len).(x:)) (perms xs)
where len = length il
欢迎任何想法......
答案 4 :(得分:1)
我认为对于其他人的建议,这是更短更优雅的变体:
<Person>
<ttId>1408</ttId>
<FirstName>RAJ</FirstName>
<NationalityValue>INDIAN</NationalityValue>
<Sex>Male</Sex>
</Person>
答案 5 :(得分:0)
它已经在标准的base库中了,所以不需要挣扎。如果你真的想看看如何做,你可以查看该库的来源。
答案 6 :(得分:0)
monads的一切都更好:
perm :: [a] -> [[a]]
perm [] = return []
perm (x:xs) = (perm xs) >>= (ins x)
where
ins :: a -> [a] -> [[a]]
ins x [] = [[x]]
ins x (y:ys) = [x:y:ys] ++ ( map (y:) (ins x ys) )
所以:你有一个函数,它在一个单词中插入字母,但是它会生成多个单词,那么如何递归地应用它呢? >>=
有帮助!
答案 7 :(得分:0)
我解决了这个问题,然后找到了讨论。这是使用递归的简短解决方案。 doPerm
的第一个参数包含适合于排列中任何位置的元素,第二个参数元素仅适合于除第一个位置以外的其他位置。
permutations :: [a] -> [[a]]
permutations xs = doPerm xs []
where
doPerm [] _ = [[]]
doPerm [y] ys = (y:) <$> doPerm ys []
doPerm (y : ys) zs = doPerm [y] (ys ++ zs) ++ doPerm ys (y : zs)
这是一个示例运行:
λ> permutations "abc"
["abc","acb","bca","bac","cba","cab"]