I assume that map (x:) gives a problem performance wise
No. map
is coded efficiently and runs in linear time, no problems here.
However, your recursion might be a problem. You're both calling sublistofsize (n-1) xs
and sublistofsize n xs
, which - given a start list sublistofsize m (_:_:ys)
- does evaluate the term sublistofsize (m-1) ys
twice, as there is no sharing between them in the different recursive steps.
So I'd apply dynamic programming to get
subsequencesOfSize :: Int -> [a] -> [[a]]
subsequencesOfSize n xs = let l = length xs
in if n>l then [] else subsequencesBySize xs !! (l-n)
where
subsequencesBySize [] = [[[]]]
subsequencesBySize (x:xs) = let next = subsequencesBySize xs
in zipWith (++) ([]:next) (map (map (x:)) next ++ [[]])
Not that appending the empty lists is the most beautiful solution, but you can see how I have used zipWith
with the displaced lists so that the results from next
are used twice - once directly in the list of subsequences of length n and once in the list of subsequences of length n+1.
Testing it in GHCI with :set +s
, you can see how this is drastically faster than the naive solutions:
*Main> length $ subsequencesOfSize 7 [1..25]
480700
(0.25 secs, 74132648 bytes)
(0.28 secs, 73524928 bytes)
(0.30 secs, 73529004 bytes)
*Main> length $ sublistofsize 7 [1..25] -- @Vixen (question)
480700
(3.03 secs, 470779436 bytes)
(3.35 secs, 470602932 bytes)
(3.14 secs, 470747656 bytes)
*Main> length $ sublistofsize' 7 [1..25] -- @Ganesh
480700
(2.00 secs, 193610388 bytes)
(2.00 secs, 193681472 bytes)
*Main> length $ subseq 7 [1..25] -- @user5402
480700
(3.07 secs, 485941092 bytes)
(3.07 secs, 486279608 bytes)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…