【问题标题】:Manipulating a list using pattern-matching使用模式匹配操作列表
【发布时间】:2019-05-06 16:50:37
【问题描述】:

我想写一个函数,它接受一个列表作为输入值并按以下方式操作它:

第 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 的列表的元素。它只会返回任何其他列表的列表而无需修改。
  • [1,2,3][2,3,1] 是旋转,而不是反转(应该是 [3,2,1])。

标签: list haskell pattern-matching


【解决方案1】:

如果内部列表总是 3 个元素,那么您可以硬编码该事实并使用如下简单的解决方案:

f :: [a] -> [[a]]
f [] = []
f (x1:x2:x3:xs) = [x2,x3,x1]:f xs
f xs = [xs]

【讨论】:

    【解决方案2】:

    您也可以使用 take 和 drop 来实现您的目标

    f [] = []
    f xs = (take 3 xs) : f2 (drop 3 xs)
    

    【讨论】:

    • 这仅涉及第 1 步。
    【解决方案3】:

    惯用的 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 一样使用它

    【讨论】:

    • 你测试过这个吗?它充满了错误。 1. 语法错误:: 后面是:: 而不是divide。 2. 您使用\case 而不启用 LambdaCase 扩展。 3. 你使用了来自第三方库的mapRight,你懒得提了。 4. process 不进行类型检查(你打电话给concat,不应该)。
    • @JosephSible,实际上不,我是用等火车的电话写的。感谢您捕获 : 而不是 :: 和不必要的 concat。其余的(lambdacase 和 mapRight)很容易用谷歌搜索。
    猜你喜欢
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多