列出在Haskell中的元组

时间:2014-11-17 01:03:39

标签: haskell

让我说我有一个这样的清单:

["Questions", "that", "may", "already", "have", "your", "correct", "answer"]

想要拥有这个:

[("Questions","that"),("may","already"),("have","your"),("correct","answer")]

这可以吗?或者这是一个糟糕的Haskell实践?

4 个答案:

答案 0 :(得分:4)

对于一个简单的方法(对于奇数个元素而言失败),您可以使用

combine :: [a] -> [(a, a)]
combine (x1:x2:xs) = (x1,x2):combine xs
combine (_:_) = error "Odd number of elements"
combine [] = []

Live demo

或者你可以使用一些复杂的方法,就像我真正想要理解的其他答案一样。

更通用:

map2 :: (a -> a -> b) -> [a] -> [b]
map2 f (x1:x2:xs) = (f x1 x2) : map2 f xs
map2 _ (_:_) = error "Odd number of elements"
map2 _ [] = []

答案 1 :(得分:2)

这是一种方法,借助辅助函数,您可以从目标列表中删除每一个元素,然后只使用zip。当列表长度奇怪时,这可能没有您想要的行为,因为问题尚未定义。

-- This is just from ghci

let my_list = ["Questions", "that", "may", "already", "have", "your", "correct", "answer"]

let dropEvery [] _ = []
let dropEvery list count = (take (count-1) list) ++ dropEvery (drop count list) count

zip (dropEvery my_list 2) $ dropEvery (tail my_list) 2

[("Questions","that"),("may","already"),("have","your"),("correct","answer")

辅助函数来自99 Questions.的问题#6,其中有许多相同构思的其他实现,可能很多具有更好的递归优化属性。

要了解dropEvery,了解每个takedrop的做法是件好事。 take k some_list获取k的第一个some_list条目。同时drop k some_list删除了第一个k条目。

如果我们想要删除每个第N个元素,这意味着我们想要保持每个(N-1)个元素的运行,然后删除一个,然后再做同样的事情,直到我们完成。

dropEvery的第一部分执行此操作:它take是第一个count-1条目,然后它将连接到从列表的其余部分获取的任何内容。

之后,它会说drop count(忘记你保留的N-1,以及你想要一直放下的1(在第N个点) - 并且在这些被丢弃之后,你可以递归地将相同的逻辑应用于剩下的任何东西。

在Haskell中以这种方式使用++可能相当昂贵,因此从性能的角度来看,这不是很好,但它是99个问题页面中可用的较短实现之一。

这是一个可以一次完成所有功能的功能,可能更具可读性:

byTwos :: [a] -> [(a,a)]
byTwos [] = []
byTwos xs = zip firsts seconds 
    where enumerated = zip xs [1..]
          firsts     = [fst x | x <- enumerated, odd $ snd x]
          seconds    = [fst x | x <- enumerated, even $ snd x]

在这种情况下,我开始说,如果我已经拥有奇数索引元素列表和偶数索引元素列表,那么使用zip很容易解决这个问题。所以,让我把它写下来,然后担心在where条款中加入它们。

where条款中,我先说zip xs [1..],它会生成[("Questions", 1), ("that", 2), ...],依此类推。

旁注:回想一下fst取元组的第一个元素,而snd取第二个元素。

然后firsts说取所有这些值的第一个元素如果第二个元素是奇数 - 这些将作为&#34;第一个&#34;在zip的最终输出元组中。

seconds说同样的事情,但只有如果第二个元素是偶数 - 这些将作为&#34;秒&#34;在zip的最终输出元组中。

如果列表长度为奇数,firsts将比seconds长一个元素,因此最终zip意味着列表的最后一个元素将被删除,并且结果将与在列表前面调用函数(除了最终元素之外的所有函数)相同。

答案 2 :(得分:0)

使用chunk中的Data.List.Split,您可以获得列表中每两个连续项目配对的预期结果,即xs命名的给定列表,

import Data.List.Split

map (\ys -> (ys!!0, ys!!1)) $ chunk 2 xs

此解决方案假设给定列表具有偶数个项目。

答案 3 :(得分:0)

简单的模式匹配可以解决这个问题:

f [] = []
f (x:y:xs) = (x,y):f(xs)

这意味着一个空列表给出一个空列表,并且一个至少两个元素的列表会返回一个包含这两个元素的列表,然后应用相同的推理...