Reputation: 3230
I have a list, I want to break the list into two, one with elements in the even indexes, and the other from the odd indexes.
breakByIndexes :: [a] -> ([a], [a])
For example:
> breakByIndexes ["A", "B", "C", "D"]
(["A", "C"], ["B", "D"]
> breakByIndexes ["A", "B", "C", "D", "E"]
(["A", "C", "E"], ["B", "D"]
I got a solution like this
breakByIndexes [] = ([], [])
breakByIndexes [e] = ([e], [])
breakByIndexes (e:o:xs) =
let (es, os) = breakByIndexes xs
in (e : es, o : os)
But I'm curious is it possible to implement without using recursion?
And is it possible to implement by composing existing functions from Data.List
?
Upvotes: 7
Views: 3980
Reputation: 714
Just remember that ASCII characters have a built in index offset by 65.
import Data.Char (ord)
ord 'A' - 65
= 0`
Without the offset, the numeric values are even and odd anyway. 'A' is 65, odd.
p l = [ [x|x<-l,odd (ord(head x))], [x|x<-l,even (ord(head x))] ]
I do love pairs. Once a data set is in pairs it becomes much easier to work with. I came across a function that creates the perfect pairs for this set, it leaves unpaired elements at the end but in the list. It does not create tuples but list pairs are okay, too. The function is chunksOf.
Here it is used as
i3 = chunksOf 2 $ words "A B C D E F G H I"
To produce the pairs we want. [["A","B"],["C","D"],["E","F"],["G","H"],["I"]]
Pairs are even-odd pairs. The odd one at the end is really lacking an odd member. All odd elements of the list are missing the odd member. The odd member extractor has to compensate with filter of pairs with less than two members.
[ map (head) l3, map (last) (filter ((>1).length) l3) ]
Produces [["A","C","E","G","I"],["B","D","F","H"]]
Pairs carry information, such as even or odd.
And to my shock and awe @DanielWagner points out that transpose
produces the desired results with the pair list. His is transpose i3
for an incredibly even more concise solution. Wow!
Edit 4/15/2018
I do hope this is final or near final.
let ls = words "A B C D E F G"
[[a|(a,b)<-zip ls [0..],bl b]|bl<-[even,odd]]
[["A","C","E","G"],["B","D","F"]]
Upvotes: 0
Reputation: 8180
EDIT: Since @DanielWagner opened the door, if it's possible to return a list instead of a tuple, an obvious solution is:
[[x | (x,i)<-zip xs [0..], i `mod` 2 == j] | j<-[0..1]]
It may be generalized to:
[[x | (x,i)<-zip xs [0..], i `mod` k == j] | j<-[0..k-1]]
For k lists of elements [[e_0, e_k, e_2k, ...], [e_1, e_k+1, e_2k+1, ...], ..., [e_k-1, e_2k-1, e_3k-1,...]].
For tuples, I leave for the record the previous code, though it's clearly worse than the other answers.
It's easy to pick even elements with a list comprehension:
Prelude> evens xs = [x | (x,i) <- zip xs [0..], even i]
Prelude> evens ["A", "B", "C", "D", "E"]
["A","C","E"]
You can do the same with odd elements. But you can also define a function that takes a filter (even
or odd
) and returns a function that will select elements:
Prelude> toFilter xs = \f -> [x | (x,i) <- zip xs [0..], f i]
Prelude> :t toFilter ["A", "B", "C", "D", "E"]
toFilter ["A", "B", "C", "D", "E"]
:: (Num t, Enum t) => (t -> Bool) -> [[Char]]
toFilter xs
takes a filter and returns a list:
Prelude> l = toFilter ["A", "B", "C", "D", "E"]
Prelude> l even
["A","C","E"]
Prelude> l odd
["B","D"]
It's also possible two define a function that takes a function like toFilter
((t -> Bool) -> [[Char]]
) and create a tuple for even
and odd
filter:
Prelude> :t \tf -> (tf even, tf odd)
\tf -> (tf even, tf odd) :: Integral a => ((a -> Bool) -> t) -> (t, t)
Now, it becomes easy to put things together:
Prelude> breakByIndexes xs = (\tf -> (tf even, tf odd)) (\f -> [x | (x,i)<-zip xs [0..], f i])
Prelude> breakByIndexes ["A", "B", "C", "D"]
(["A","C"],["B","D"])
Prelude> breakByIndexes ["A", "B", "C", "D", "E"]
(["A","C","E"],["B","D"])
Less elegant than @elemx80s, but does the job...
Upvotes: 1
Reputation: 10091
My favourite version of this function uses foldr
pairs = foldr (\x ~(ys,zs) -> (x:zs,ys)) ([],[])
It works by swapping the tuple around on each item in the list. Inside the closure:
\x ~(odds,evens) -> (x:evens, odds)
You add on the x
, which means that all of the rest of the elements in the evens list now become the odd-numbered elements.
What's the ~
for? It makes the pattern-match lazy. Without it, you'll force the tuple. So, for instance, if I wrote:
(head . fst . pairs) [1..]
It would not work without the ~
. You could achieve the same effect by writing:
pairs = foldr (\x yszs -> (x:snd yszs,fst yszs)) ([],[])
Or:
pairs = foldr (\x -> uncurry (\ys zs -> (x:zs,ys))) ([],[])
Upvotes: 6
Reputation: 152837
Here is another way. Unlike the others answers at the time of posting, it naturally generalizes to other moduli than 2.
Data.List Data.List.Split> transpose . chunksOf 2 $ "ABCDE"
["ACE","BD"]
Upvotes: 5
Reputation: 3504
Yes you are right, using the partition
function from Data.List
.
Prelude Data.List> (s, u) = partition (even . fst) (zip [0 .. ] "ABCD")
Prelude Data.List> (_, s2) = unzip s
Prelude Data.List> (_, u2) = unzip u
Prelude Data.List> (s2, u2)
("AC","BD")
How I found this? Go to Hoogle and fill in [a] -> ([a], [a])
.
Upvotes: 8