如何选择偶数索引和奇数索引中的元素?

时间:2018-04-09 20:34:58

标签: haskell recursion

我有一个列表,我希望将列表分成两个,一个包含偶数索引中的元素,另一个包含奇数索引中的元素。

breakByIndexes :: [a] -> ([a], [a])

例如:

> breakByIndexes ["A", "B", "C", "D"]
(["A", "C"], ["B", "D"]

> breakByIndexes ["A", "B", "C", "D", "E"]
(["A", "C", "E"], ["B", "D"]

我得到了这样的解决方案

breakByIndexes [] = ([], [])
breakByIndexes [e] = ([e], [])
breakByIndexes (e:o:xs) =
  let (es, os) = breakByIndexes xs
   in (e : es, o : os)

但我很好奇是否可以在不使用递归的情况下实现? 是否可以通过编写Data.List

中的现有函数来实现

5 个答案:

答案 0 :(得分:6)

是的,你是对的,使用partition中的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")

我是怎么发现的?转到Hoogle并填写[a] -> ([a], [a])

答案 1 :(得分:6)

我最喜欢的此功能版本使用foldr

pairs = foldr (\x ~(ys,zs) -> (x:zs,ys)) ([],[])

它通过在列表中的每个项目上交换元组来工作。封闭内部:

\x ~(odds,evens) -> (x:evens, odds)

您添加x,这意味着现在,evens列表中的所有其他元素成为奇数元素。

~的内容是什么?它使模式匹配 lazy 。没有它,你就会强制元组。所以,例如,如果我写道:

(head . fst . pairs) [1..]

如果没有~,它就无法运作。你可以通过写:

来达到同样的效果
pairs = foldr (\x yszs -> (x:snd yszs,fst yszs)) ([],[])

或者:

pairs = foldr (\x -> uncurry (\ys zs -> (x:zs,ys))) ([],[])

答案 2 :(得分:5)

这是另一种方式。与发布时的其他答案不同,它自然地推广到2以外的其他模数。

Data.List Data.List.Split> transpose . chunksOf 2 $ "ABCDE"
["ACE","BD"]

答案 3 :(得分:0)

编辑:由于@DanielWagner打开了大门,如果可以返回列表而不是元组,那么显而易见的解决方案是:

[[x | (x,i)<-zip xs [0..], i `mod` 2 == j] | j<-[0..1]]

可以概括为:

[[x | (x,i)<-zip xs [0..], i `mod` k == j] | j<-[0..k-1]]

对于k个元素列表[[e_0,e_k,e_2k,...],[e_1,e_k + 1,e_2k + 1,...],...,[e_k-1,e_2k-1, e_3k-1,...]]。

对于元组,我留下以前的代码记录,虽然它明显比其他答案更差。

使用列表理解很容易选择偶数元素:

Prelude> evens xs = [x | (x,i) <- zip xs [0..], even i]
Prelude> evens ["A", "B", "C", "D", "E"]
["A","C","E"]

您可以对奇数元素执行相同操作。但是你也可以定义一个带过滤器(evenodd)并返回一个选择元素的函数的函数:

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采用过滤器并返回一个列表:

Prelude> l = toFilter ["A", "B", "C", "D", "E"]
Prelude> l even
["A","C","E"]
Prelude> l odd
["B","D"]

也可以两个定义一个函数,它接受toFilter(t -> Bool) -> [[Char]])之类的函数,并为evenodd过滤器创建一个元组:< / p>

Prelude> :t \tf -> (tf even, tf odd)
\tf -> (tf even, tf odd) :: Integral a => ((a -> Bool) -> t) -> (t, t)

现在,把事情放在一起很容易:

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"])

不如@ elemx80s优雅,但做得好......

答案 4 :(得分:0)

请记住,ASCII字符的内置索引偏移量为65。

导入Data.Char(ord)

ord 'A' - 65 = 0`

没有偏移量,数值无论如何都是偶数和奇数。 'A'是65,奇数。

p l = [ [x|x<-l,odd (ord(head x))], [x|x<-l,even (ord(head x))] ]

我爱对。一旦数据集成对,就可以更容易地使用。我遇到了一个为这个集合创建完美对的函数,它在末尾但在列表中留下了不成对的元素。它不会创建元组,但列表对也可以。该函数是chunksOf。

这里用作

i3 = chunksOf 2 $ words "A B C D E F G H I"

生成我们想要的对。 [["A","B"],["C","D"],["E","F"],["G","H"],["I"]]

对是偶数对。最后一个奇怪的人真的缺少一个奇怪的成员。列表中的所有奇数元素都缺少奇数成员。奇数成员提取器必须补偿少于两个成员的对的过滤器。

[ map (head) l3,   map (last) (filter ((>1).length) l3) ]

制作[["A","C","E","G","I"],["B","D","F","H"]] 对携带信息,例如偶数或奇数。

令我震惊和敬畏的@DanielWagner指出transpose使用配对列表产生了预期的结果。他的transpose i3是一个非常简洁的解决方案。哇!

编辑4/15/2018

我希望这是最终的或接近最终的。

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”]]