使用模式匹配操作列表

时间:2019-05-06 16:50:38

标签: list haskell pattern-matching

我想编写一个将列表作为输入值并按以下方式操作的函数:

第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

3 个答案:

答案 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