I am having difficulty figuring out how to split a list of Ints into a tuple containing two new lists, such that every element (starting with first) goes into the first list and every other element in the second.
Like so:
split [] = ([],[])
split [1] = ([1],[])
split [1,2] = ([1],[2])
split [1,2,3] = ([1,3],[2])
split [1,2,3,4] = ([1,3],[2,4])
I'm trying to accomplish this recursively(with guards) and only using the single argument xs
This is my approach that keeps getting error messages:
split :: [Int] -> ([Int],[Int])
split xs | length(xs) == 0 = ([],[])
| length(xs) == 1 = (xs !! 0 : [],[])
| length(xs) == 2 = (xs !! 0 : [], xs !! 1 : [])
| otherwise = (fst ++ xs !! 0, snd ++ xs !! 1) ++ split(drop 2 xs))
The standard library in Haskell provides a zip function, which combines the elements of two lists into a single list of tuples. I decided to implement my own version, named zip prime (actually, zip’ since Haskell allows a function name to include the prime ( ‘) symbol).
Make a new list containing just the first N elements from an existing list. Split a list into two smaller lists (at the Nth position). (Returns a tuple of two lists.)
You have to split the list in two, remove the element from one list, and then join them back together, like this: (Related: tail xs removes the first element.) (Related: init xs removes the last element. Slow if the list is big.) Delete elements that meet some condition. Haskell has a function called filter which will do this for you.
To split a list of one element, produce a list with that element, and a list with no elements.
Your split
function returns a pair, but in the last case you are using ++
on the result of split
. That will be a type error, since ++
works on lists, not pairs. There is also a type error because fst
and snd
are functions to pick out the elements of a pair, but you are using them is a strange way.
Furthermore, use pattern matching instead of using length. Also, the case where you test if the length is 2 is not needed, since the general case removes 2 elements which takes you down to the base case of the empty list.
You can also make your function more general by using a type variable a
instead of Int
in the type.
[Edit]: Added code
split :: [a] -> ([a], [a])
split [] = ([], [])
split [x] = ([x], [])
split (x:y:xys) = (x:xs, y:ys) where (xs, ys) = split xys
Another way to do this is with mutual recursion. It comes out very easy to read:
split xs = (odds xs, evens xs)
odds (x:xs) = x : evens xs
odds xs = []
evens xs = odds (drop 1 xs)
split :: [a] -> ([a], [a])
split xs | null xs = ([], [])
| otherwise = (head xs : snd pair, fst pair)
where pair = split (tail xs)
But you should be using a fold:
split :: [a] -> ([a], [a])
split = foldr (\x (ys, zs) -> (x : zs, ys)) ([], [])
The Haskell Blow Your Mind wiki, has some one liners:
-- splitting in two (alternating)
-- "1234567" -> ("1357", "246")
-- the lazy match with ~ is necessary for efficiency, especially enabling
-- processing of infinite lists
foldr (\a ~(x,y) -> (a:y,x)) ([],[])
(map snd *** map snd) . partition (even . fst) . zip [0..]
transpose . unfoldr (\a -> toMaybe (null a) (splitAt 2 a))
Two alternative versions:
split = conv . map (map snd) . groupWith (even.fst) . zip [0..] where
conv [xs,ys] = (xs,ys)
split xs = (ti even xs, ti odd xs) where
ti f = map snd . filter (f.fst) . zip [0..]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With