The following function generates every length-2 list [a,b]
using the integers from 1
to top
for which a < b
.
ascendingPairs top = [ [x,y]
| x <- [1..top],
y <- [x+1..top] ]
Here it is in action:
> mapM_ (putStrLn . show) $ f 4
[1,2]
[1,3]
[1,4]
[2,3]
[2,4]
[3,4]
ascendingPairs
is defined only for the special case that the lists produced should have length 2. What if I don't know how long the output lists should be, and want that length to be an argument?
Here's a dumb way to provide for an additional length argument:
partial :: Int -> Int -> [[Int]]
partial 1 top = [ [x]
| x <- [1..top] ]
partial 2 top = [ [x,y]
| x <- [1..top],
y <- [x+1 .. top] ]
partial 3 top = [ [x,y,z]
| x <- [1..top],
y <- [x+1 .. top],
z <- [y+1 .. top] ]
partial 4 top = ...
partial 5 top = ...
...
Writing partial
that way, it would take literally forever to cover every case.
Here's a way to cover all cases. (This way actually does need the list monad.)
slow :: Int -> Int -> [[Int]]
slow top size =
filter monotonicAscending $
mapM (\x -> [1..top]) [1..size]
monotonicAscending :: [Int] -> Bool
monotonicAscending (a:b:rest) = a < b
&& monotonicAscending (b:rest)
monotonicAscending _ = True
slow
saves human time, but at the expense of a ton of machine time, because it generating so many scales that filter monotonicAscending
immediately drops. Computing length $ f 41 7
takes at least a minute, maybe hours (I cut it off). For my purposes I'd actually prefer partial
to slow
.
Eventually I did find a way, but I feel like I'm brute forcing it.
monoAscending :: Int -> Int -> [[Int]]
monoAscending top size =
map reverse $
incrementNTimes top (size-1) $ [ [a]
| a <- [1..top] ]
incrementNTimes :: Int -> Int -> [[Int]] -> [[Int]]
incrementNTimes top 0 lists = lists
incrementNTimes top n lists = let
x :: [[Int]]
x = concatMap (increments top) lists
in incrementNTimes top (n-1) x
-- | All the ways of prepending a bigger element to the input list.
-- This assumes the input list is in descending order.
increments :: Int -> [Int] -> [[Int]]
increments top (a:as) = [ b:a:as
| b <- [a+1 .. top]]
It works:
> mapM_ (putStrLn . show) $ monoAscending 4 3
[1,2,3]
[1,2,4]
[1,3,4]
[2,3,4]
But is there a better way?
I recommend directly extending your very first idea, for partial
. Just... use recursion!
notPartial 0 bot top = [[]]
notPartial n bot top = [ x:xs
| x <- [bot..top]
, xs <- notPartial (n-1) (x+1) top
]
Then you can make an alias that fixes bot
if you like.
monoAscending n top = notPartial n 1 top
Try it out:
> monoAscending 3 5
[[1,2,3],[1,2,4],[1,2,5],[1,3,4],[1,3,5],[1,4,5],[2,3,4],[2,3,5],[2,4,5],[3,4,5]]