为什么Haskell中的groupBy需要两个变量来检查

时间:2017-02-19 16:14:16

标签: haskell

我在Haskell中经历了groupBy,我理解的是groupBy函数接受List和条件,并根据指定的条件对元素进行分组。

let value = [-3,-5,-1,1,1,1,2,3,-1,-2]
groupBy (\x y -> (x > 0) == (y > 0)) value

它工作正常,但为什么我们应该在lambda函数中给出两个变量?为什么“groupBy(> 0)值”不起作用?两个条件都应该相同吗?如果它们不同会发生什么。请用例子说明。

3 个答案:

答案 0 :(得分:5)

Lignum's answer解释了为什么groupBy谓词有两个参数。换句话说,groupBy设置边界,你需要两个边来定义边界。

  

两个条件都应该相同吗?如果它们不同会发生什么。

是的,他们应该。正如Data.List documentation注意到......

  

假设谓词定义了等价。

换句话说,如果p :: a -> a -> Bool是您传递给groupBy的谓词,那么所有......

  • p x x = True
  • p x y = p y x
  • 如果p x y && p y zp x z

......应该坚持。如果不是这样的话会发生奇怪的事情。例如,我们可能一开始认为......

groupBy (\x y -> (x > 0) == (y < 0)) [-1,2,-3,-4,2,1,2,-3,1,1,2]

...将产生由零和零以下元素交替组成的组。相反,我们得到了虚假的结果:

GHCi> groupBy (\x y -> (x > 0) == (y < 0)) [-1,2,-3,-4,2,1,2,-3,1,1,2]
[[-1,2],[-3],[-4,2,1,2],[-3,1,1,2]]

(它出错的原因是,在groupBy方面实施span可以利用谓词为等价的保证。

最后,有一种更好的方式来编写groupBy谓词:使用Data.Function中的on

GHCi> groupBy ((==) `on` (> 0)) value
[[-3,-5,-1],[1,1,1,2,3],[-1,-2]]

(g `on` f) x y = g (f x) (f y)。你甚至可以定义:

-- A more general type: Eq b => (a -> b) -> [a] -> [[a]]
groupBySingle :: (a -> Bool) -> [a] -> [[a]]
groupBySingle p = groupBy ((==) `on` p)

答案 1 :(得分:2)

  

[...]我理解的方式是groupBy函数接受List和条件,并根据指定的条件对元素进行分组。

不完全是,它根据指定的条件对相邻的元素进行分组。请注意您的代码如何评估此列表:

[[-3,-5,-1],[1,1,1,2,3],[-1,-2]]

而不是此列表:

[[-3,-5,-1,-1,-2],[1,1,1,2,3]]

因此,传递给groupBy的lambda表达式决定了两个相邻的元素是否应该在一个组中。

答案 2 :(得分:2)

groupBy通过检查当前元素是否属于基于前一个元素的组来进行操作。

您想要的行为最好通过单独的功能来解释,重复应用spanbreak

myFunc :: (a -> Bool) -> [a] -> [[a]]
myFunc _ [] = []
myFunc p xs = satp : notsatp : myFunc p rest
  where
    (satp, rest') = span p xs
    (notsatp, rest) = break p rest'