让我说我有一个这样的清单:
["Questions", "that", "may", "already", "have", "your", "correct", "answer"]
想要拥有这个:
[("Questions","that"),("may","already"),("have","your"),("correct","answer")]
这可以吗?或者这是一个糟糕的Haskell实践?
答案 0 :(得分:4)
对于一个简单的方法(对于奇数个元素而言失败),您可以使用
combine :: [a] -> [(a, a)]
combine (x1:x2:xs) = (x1,x2):combine xs
combine (_:_) = error "Odd number of elements"
combine [] = []
或者你可以使用一些复杂的方法,就像我真正想要理解的其他答案一样。
更通用:
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
,了解每个take
和drop
的做法是件好事。 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)
这意味着一个空列表给出一个空列表,并且一个至少两个元素的列表会返回一个包含这两个元素的列表,然后应用相同的推理...