我想编写一个将列表作为输入值并按以下方式操作的函数:
第1步:将列表的每3个元素放入一个子列表。 如果剩余的元素少于3个,则其余的元素将放到一个特定的子列表中,该子列表与步骤2中无关。
第2步:颠倒所创建子列表中元素的顺序。 第一个元素应放置在第三个元素的位置,第二个元素应放置在第一个元素的位置,第三个元素应放置在第二个元素的位置。 ([1,2,3]转换为[2,3,1])
示例:
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]
-- should be transformed to
[[2,3,1],[5,6,4],[8,9,7],[11,12,10],[14,15,13],[16,17]]
到目前为止,我发现以下方法可以将每个3个元素放到子列表中,但是我不确定如何更改每个子列表中元素的顺序以符合要求。
splitEvery :: Int -> [a] -> [[a]]
splitEvery _ [] = []
splitEvery n xs = as : splitEvery n bs
where (as,bs) = splitAt n xs
答案 0 :(得分:0)
如果内部列表始终是3个元素,那么您可以对该事实进行硬编码并使用像这样的简单解决方案:
f :: [a] -> [[a]]
f [] = []
f (x1:x2:x3:xs) = [x2,x3,x1]:f xs
f xs = [xs]
答案 1 :(得分:0)
您也可以通过拖放来实现目标
f [] = []
f xs = (take 3 xs) : f2 (drop 3 xs)
答案 2 :(得分:-1)
惯用的Haskell比这里的其他答案要好得多。基本上,尝试始终设计API,使其包含不会发生极端情况的证明。列表上的硬编码文字或硬编码类似翻转的操作(不保证其长度)总是很糟糕。
{-# LANGUAGE LambdaCase #-}
divide : [a] -> [Either [a] (a,a,a)]
divide = \case
[] -> []
t1:t2:t3:ts -> Right (t1,t2,t3) : divide ts
ts -> [Left ts]
process :: [Either [a] (a,a,a)] -> [[a]]
process = fmap (flatten . flipEls) where
flipEls = fmap $ \(t1,t2,t3) -> [t2,t1,t3]
flatten = either id id
现在,您可以像process . divide