Haskell 哈斯克尔:令人惊讶的行为;groupBy“;

Haskell 哈斯克尔:令人惊讶的行为;groupBy“;,haskell,combinators,Haskell,Combinators,我试图弄清楚库函数groupBy(from Data.List)的行为,它声称通过作为第一个参数传入的“相等测试”函数对列表的元素进行分组。类型签名表明相等性测试只需要具有类型 (a -> a -> Bool) 但是,当我使用(时,请看一下ghc的实现: 现在比较这两个输出: Prelude List> groupBy (<) [1, 2, 3, 2, 4, 1, 5, 9] [[1,2,3,2,4],[1,5,9]] Prelude List> groupBy

我试图弄清楚库函数groupBy(from Data.List)的行为,它声称通过作为第一个参数传入的“相等测试”函数对列表的元素进行分组。类型签名表明相等性测试只需要具有类型

(a -> a -> Bool)
但是,当我使用(时,请看一下ghc的实现:

现在比较这两个输出:

Prelude List> groupBy (<) [1, 2, 3, 2, 4, 1, 5, 9]
[[1,2,3,2,4],[1,5,9]]
Prelude List> groupBy (<) [8, 2, 3, 2, 4, 1, 5, 9]
[[8],[2,3],[2,4],[1,5,9]]

事实上,“问题在于Haskell报告中的
groupBy
的参考实现将元素与第一个元素进行比较,因此这些组并没有严格地增加(它们只需要都大于第一个元素)。您需要的是一个版本的
groupBy
,它可以测试相邻元素,如实现。

我只想指出,groupBy函数还要求在应用之前对列表进行排序

例如:

equalityOp :: (a, b1) -> (a, b2) -> Bool
equalityOp x y = fst x == fst y

testData = [(1, 2), (1, 4), (2, 3)]

correctAnswer = groupBy equalityOp testData == [[(1, 2), (1, 4)], [(2, 3)]]

otherTestData = [(1, 2), (2, 3), (1, 4)]

incorrectAnswer = groupBy equalityOp otherTestData == [[(1, 2)], [(2, 3)], [(1, 4)]]
之所以出现这种行为,是因为groupBy在其定义中使用了span。为了获得合理的行为,而不依赖于我们以任何特定顺序拥有基础列表,我们可以定义一个函数:

groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy' eq []     = []
groupBy' eq (x:xs) = (x:similarResults) : (groupBy' eq differentResults)
    where similarResults   = filter (eq x) xs
          differentResults = filter (not . eq x) xs

谢谢。我没有意识到文档要求平等测试是一个等价关系。它没有说它必须是一个等价关系。事实上,使用非等价关系可以做一些有用的事情。
Prelude List> groupBy (<) [1, 2, 3, 2, 4, 1, 5, 9]
[[1,2,3,2,4],[1,5,9]]
Prelude List> groupBy (<) [8, 2, 3, 2, 4, 1, 5, 9]
[[8],[2,3],[2,4],[1,5,9]]
groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy' _   []                        = []
groupBy' _   [x]                       = [[x]]
groupBy' cmp (x:xs@(x':_)) | cmp x x'  = (x:y):ys
                           | otherwise = [x]:r
  where r@(y:ys) = groupBy' cmp xs
[1, 2, 3, 2, 4, 1, 5, 9] ->
[[1,2,3], [2,4], [1,5,9]]
equalityOp :: (a, b1) -> (a, b2) -> Bool
equalityOp x y = fst x == fst y

testData = [(1, 2), (1, 4), (2, 3)]

correctAnswer = groupBy equalityOp testData == [[(1, 2), (1, 4)], [(2, 3)]]

otherTestData = [(1, 2), (2, 3), (1, 4)]

incorrectAnswer = groupBy equalityOp otherTestData == [[(1, 2)], [(2, 3)], [(1, 4)]]
groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy' eq []     = []
groupBy' eq (x:xs) = (x:similarResults) : (groupBy' eq differentResults)
    where similarResults   = filter (eq x) xs
          differentResults = filter (not . eq x) xs