patternMinor
Next lexicographic permutation of a list
Viewed 0 times
permutationlistlexicographicnext
Problem
I began learning Haskell last week. I wrote the following to get the next lexicographic permutation of a list. It works perfectly but I feel like I'm just writing a c# method. My long list of where commands just feels like an imperative approach. I would appreciate any feedback if this solution is indeed the way most people would write something like this in haskell:
-- Finds the end of a list that is monotonically decreasing (swap)
-- Pivot is the index just before it. (pivot)
-- exchanges the smallest value > pivot in swap with pivot
-- reverses the swap with the pivot in it, and concatenates the new permutation
nextPerm :: (Ord a,Num a) => [a] -> [a]
nextPerm [] = []
nextPerm a = if pivotIndex == (-1) then a
else prePivot ++ [swapVal] ++ swapWithPivot
where
swap = foldl (\acc x -> if x if x > pivotVal then x else acc) swap
swapWithPivot = insert pivotVal $ delete swapVal $ reverse swapSolution
I'm a beginner at Haskell as well, so take my opinion with a big rock of salt.
I wouldn't use
I like to name lists
Lists are weak at random access, but kind of good at being sliced up; therefore I'd definitely drop the whole indexing thing.
Anyway, I will include how I'd write it, hopefully it'll give you some new ideas.
If you want to test an implementation, issue
If it works, the result will be
nextPerm :: (Ord a, Num a) => [a] -> [a]
nextPerm [] = []
nextPerm aI wouldn't use
a as a type variable and a variable at the same time. I like to name lists
xs, ys, cs, etc. I feel this is more descriptive, and it works well with pattern-matching, e.g. (x:xs), where x is the head of the list, xs is the tail.swap = foldl (\acc x -> if x e.g. :t span - type signatures (and names) will usually tell you what a function does.Lists are weak at random access, but kind of good at being sliced up; therefore I'd definitely drop the whole indexing thing.
Anyway, I will include how I'd write it, hopefully it'll give you some new ideas.
nextPerm' :: Ord a => [a] -> [a]
nextPerm' = uncurry (++) . uncurry swap
. (fmap fst *** fmap fst)
. span (uncurry (>=))
. () zip (\ls -> head ls:ls)
. reverse
where swap rpost [] = ([], rpost)
swap rpost (pivot:rpre) = (reverse rpre, ins . span (<= pivot) $ rpost)
where ins (le,[]) = le ++ [pivot]
ins (le,a:as) = a:le ++ pivot:asIf you want to test an implementation, issue
(sort . permutations $ [1,2,3,4,5]) == (take 120 $ iterate nextPerm [1,2,3,4,5])If it works, the result will be
True.Code Snippets
nextPerm :: (Ord a, Num a) => [a] -> [a]
nextPerm [] = []
nextPerm anextPerm :: Ord a => [a] -> [a]
nextPerm xs
| null xs || pivotIndex == -1 = xs
| otherwise = prePivot ++ swapVal : swapWithPivot
where pivotIndex = length xs - length swap - 1
prePivot = take pivotIndex xs
pivotVal = xs !! pivotIndex
swapVal = foldl1 (\acc x -> if x > pivotVal then x else acc) swap
swapWithPivot = insert pivotVal . delete swapVal . reverse $ swap
swap = reverse . fmap fst
. takeWhile (uncurry (>=))
. (<*>) zip (\ls -> head ls:ls)
. reverse $ xsnextPerm' :: Ord a => [a] -> [a]
nextPerm' = uncurry (++) . uncurry swap
. (fmap fst *** fmap fst)
. span (uncurry (>=))
. (<*>) zip (\ls -> head ls:ls)
. reverse
where swap rpost [] = ([], rpost)
swap rpost (pivot:rpre) = (reverse rpre, ins . span (<= pivot) $ rpost)
where ins (le,[]) = le ++ [pivot]
ins (le,a:as) = a:le ++ pivot:as(sort . permutations $ [1,2,3,4,5]) == (take 120 $ iterate nextPerm [1,2,3,4,5])Context
StackExchange Code Review Q#159546, answer score: 2
Revisions (0)
No revisions yet.