7

I have the following function which takes a list and returns two sublists split at a given element n. However, I only need to split it in half, with odd length lists having a larger first sublist

splitlist :: [a] -> Int -> ([a],[a])
splitlist [] = ([],[])
splitlist l@(x : xs) n | n > 0     = (x : ys, zs)
               | otherwise = (l, [])
    where (ys,zs) = splitlist xs (n - 1)

I know I need to change the signature to [a] -> ([a],[a]), but where in the code should I put something like length(xs) so that I don't break recursion?

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
kqualters
  • 306
  • 1
  • 3
  • 13
  • See also http://stackoverflow.com/questions/27090347/haskell-novice-trouble-with-splitting-a-list-in-half – dfeuer Mar 30 '16 at 03:02

3 Answers3

11

In a real program you should probably use

splitlist :: [a] -> ([a], [a])
splitlist xs = splitAt ((length xs + 1) `div` 2) xs

(i.e. something along the lines of dreamcrash's answer.)

But if, for learning purposes, you're looking for an explicitly recursive solution, study this:

splitlist :: [a] -> ([a], [a])
splitlist xs = f xs xs where
    f (y : ys) (_ : _ : zs) =
        let (as, bs) = f ys zs
        in (y : as, bs)
    f (y : ys) (_ : []) = (y : [], ys)
    f ys [] = ([], ys)
dfeuer
  • 48,079
  • 5
  • 63
  • 167
dave4420
  • 46,404
  • 6
  • 118
  • 152
  • Btw, I don't if it is because I am compiling on codepad, but length xs + 1 'div' 2 it is necessary to put length (xs + 1) 'div' 2 – dreamcrash Dec 09 '12 at 14:00
  • No, it's because I didn't test my code. Thanks for spotting it. – dave4420 Dec 09 '12 at 14:02
  • The tortoise-and-hare solution is quite a lot better than the `splitAt`-based one if the list is long. It performs one pass instead of two, and it produces the first component lazily. I'm not sure why you recommend the other implementation. – dfeuer Mar 30 '16 at 04:59
9

You can do it using take and drop:

splitlist :: [a] -> ([a],[a])
splitlist [] = ([],[])
splitlist l  = let half = (length(l) +1)`div` 2
               in (take half l, drop half l)

or you can take advantage of the function splitAt:

splitlist list = splitAt ((length (list) + 1) `div` 2) list
dreamcrash
  • 47,137
  • 25
  • 94
  • 117
0

You can do this by using the take and drop built-in functions. But if you want something that can be done with all self written functions try this:

dropList :: Int -> [Int] -> [Int]
dropList 0 [] = []
dropList 0 (x:xs) = x:xs
dropList y [] = []
dropList y (x:xs) = dropList (y-1) xs

takeList :: Int -> [Int] -> [Int]
takeList 0 [] = []
takeList 0 (x:xs) = []
takeList y [] = []
takeList y (x:xs) 
  | y <= length (x:xs) =  x : takeList (y-1) xs
  | otherwise = []

split :: Int -> [Int] -> ([Int],[Int])
split 0 [] = ([],[])
split y [] = ([],[])
split y (x:xs) = (takeList y (x:xs), dropList y (x:xs))

main = do
  print (split 4 [1,2,3,4,5,6])
Rhys Broughton
  • 420
  • 5
  • 9