Haskell 哈斯克尔:令人惊讶的行为;groupBy“;
我试图弄清楚库函数groupBy(from Data.List)的行为,它声称通过作为第一个参数传入的“相等测试”函数对列表的元素进行分组。类型签名表明相等性测试只需要具有类型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
(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