如何在haskell中为列表元素编写groupBy函数?

时间:2018-03-26 11:39:21

标签: haskell functional-programming

它的签名必须如下所示:

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]

我的函数应该将输入列表的元素分组到输出列表中,这些元素基于元素是否相等或者是否存在严格单调的递增部分列表。

终止的例子:

-- groupBy (==) groups the equal elements
groupBy (==) [0, 0, 1, 1, 2, 2] == [[0, 0], [1, 1], [2, 2]]
groupBy (==) [0, 1, 2] == [[0], [1], [2]]

-- groupBy (<) returns the strictly monotonous incrementing partlists
groupBy (<) [0, 1, 2, 1, 2, 3] == [[0,1,2],[1,2,3]]
groupBy (<) [3, 4, 5] == [[3, 4, 5]]

groupBy (>=) [3, 3, 1, 5] == [[3,3,1],[5]] --- monotonous decrementing

-- partlists, where the consecutive elements' difference is 1:
groupBy (\x y -> abs (x - y) == 1) [0, 1, 3, 4] == [[0, 1], [3, 4]]
groupBy (\x y -> abs (x - y) == 1) [1, 2, 3, 2, 1, 10, 11] == [[1,2,3,2,1],[10,11]]

提前感谢您的帮助:)

1 个答案:

答案 0 :(得分:0)

看看这里必须进行的比较。您提供了一个函数f :: a -> a -> Bool,只要它保持为真,就会对每个项目进行分组,换句话说:

groupBy (>=) [3, 3, 1, 5, 4]
                [3]
3 >= 3 = True   [3, 3]  -- add RHS value
3 >= 1 = True   [3, 3, 1]
1 >= 5 = False  -- new group!

                [5]
5 >= 4 = True   [5, 4]

这看起来像是递归给我。你的内部函数应该保留一个累加器,查看列表中的每个元素并决定A)将它包含在当前累加器中或B)给累加器返回并根据f的结果开始一个新的累加器。

groupBy定义为执行实际工作的辅助函数的包装器可能会有所帮助。

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy f (x:xs) = go f [x] xs
  where
  -- base case: no values left to check
  go _ acc       [] = [acc]

  -- recursive case
  go f acc@(y:_) (x:xs)
    | y `f` x   =  go f (x:acc) xs    -- add to current group
    | otherwise =  acc : go f [x] xs  -- start a new group

请注意,上面有一个微妙的错误,我留下来用于说明目的(也因为我先用这种方式编写它,所以你也可以!)