Learn You a Haskell显示groupBy
功能:
ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5,
29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
在groupBy
的第一个参数中,lambda的2个参数的含义是什么:x
和y
?
答案 0 :(得分:2)
这些是要比较的变量。您知道group
将相等的相邻值放在一起。要确定什么是相等的值,它使用比较函数。 group
依赖于Eq
类型类的实例。但是groupBy
允许您选择如何比较相邻的值。
答案 1 :(得分:1)
如果我们查看groupBy
的类型:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy
的第一个参数是一个函数,它接受a
类型的两个参数并返回Bool
。你可以等同地把它写成
groupBy comparer values where comparer x y = (x > 0) == (y > 0)
\x y ->
部分只是说lambda函数有两个名为x
和y
的参数,就像任何其他函数声明一样。
查看此表达式的最简单方法是运行它:
ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
仔细观察,您可以看到每个子列表是按正数还是负数进行分组。 groupBy
函数按给定条件对列表的元素进行分组,但仅按顺序排列。例如:
ghci> groupBy (\x y -> x == y) [1, 1, 2, 2, 2, 3, 3, 4]
[[1,1],[2,2,2],[3,3],[4]]
ghci> groupBy (\x y -> x == y) [1, 1, 2, 2, 2, 3, 3, 1]
[[1,1],[2,2,2],[3,3],[1]]
在第二个示例中,请注意1
个尚未组合在一起,因为它们并不相邻。
答案 2 :(得分:1)
在这样的情况下,最好直接前往源头! groupBy
是Data.List
的一部分,因此您可以在Hackage上找到base
个包。如果您不知道某个函数所在的包,请在Hoogle中搜索该函数,然后单击要在Hackage上的Haddocks中获取的名称。当您查看Haddock文档时,通常会有一个" Source"链接在函数类型定义的右侧,以转到定义。这是groupBy
的来源。
我已经在这里复制了这个定义以逐步完成它。
-- | The 'groupBy' function is the non-overloaded version of 'group'.
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
首先,顶部的文档行告诉我们groupBy
是group
的非重载版本,这是base
中非常常见的模式。您可以查看group
以找出最简单的分组功能,然后您可以将 - By
版本理解为允许您提供自己的谓词(如果您想比较不同于类型的Eq
实例,或者您尝试执行的任何其他操作。
基本情况是微不足道的,但如果你不知道span
做什么(再次击中Hackage的时间),递归步骤可能会有点混乱。 span
获取谓词和列表,并返回在与谓词不匹配的第一个元素之前断开的一对(2元组)列表(它类似break
但是(没有)否定)。
所以现在你应该能够把所有这些放在一起,看看groupBy
通过隔离"等于"的元素的运行,将列表中的元素组合在一起。到该运行中的第一个元素。请注意,它不是成对比较元素(之前我被烧过)所以不要假设传递给谓词函数的两个元素在列表中是相邻的!
答案 3 :(得分:0)
让我们从group
开始。该功能简单地将所有相同的相邻元素组合在一起。如,
group [0,1,2,3,3,4] = [[0],[1],[2],[3,3],[4]]
GHC将group
定义如下:
group :: Eq a => [a] -> [[a]]
group = groupBy (==)
也就是说,相等性测试是隐含的。使用groupBy
as;
groupBy (\x y -> x == y) [0,1,2,3,3,4] = [[0],[1],[2],[3,3],[4]]
现在,让我们看一下GHC如何定义groupBy
:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
eq
用于根据与第一个元素的比较来拆分列表的其余部分。然后在未通过比较的元素列表上递归调用groupBy
。请注意,(x:ys)
是第一个元素的连接,以及满足比较条件的元素列表。此外,span
函数将在不满足测试条件的第一个元素处开始第二个列表。
因此,在给定的示例中,当您达到值0.4时,必须开始一个新列表,因为0.4将是上述定义中zs
的第一个元素。
答案 4 :(得分:0)
groupBy
根据某些“规则”将列表分组。在groupBy (\x y -> x `someComparison` y) someList
中,x
是“当前”组的第一个元素,y
是someList
的元素。 groupBy
遍历someList
,因此每一步y
都成为someList
的下一个元素。当谓词返回False
时,新组启动。 y
成为新组的第一个成员。迭代继续,此新组的第一个成员现在变为x
,someList
的下一个元素变为y
。
groupBy
NOT 成对比较元素(1 st 与2 nd ,2 nd 使用3 rd 等),而是将列表中的每个元素与当前正在填充的组的第一个元素进行比较。例如:
groupBy (\x y -> x < y) [1,2,3,2,1] -- returns: [[1,2,3,2],[1]]
一步一步groupBy
:
[[1,2]]
[[1,2,3]]
[[1,2,3,2]]
[[1,2,3,2],[1]]
练习:要了解groupBy
的工作原理,请尝试弄清楚以下表达式如何使用笔和纸返回结果:
groupBy (\x y -> x < y) [1,2,3,4,5,4,3,2,1] -- [[1,2,3,4,5,4,3,2],[1]]
groupBy (\x y -> x < y) [1,3,5,2,1] -- [[1,3,5,2],[1]]
groupBy (\x y -> x <= y) [3,5,3,2,1,0,1,0] -- [[3,5,3],[2],[1],[0,1,0]]
groupBy (\x y -> x <= y) [1,2,3,2,1] -- [[1,2,3,2,1]]
同样,理解groupBy
行为的关键是要记住,在每一步中,它将当前组的第一个元素与列表的连续元素进行比较。时刻谓词返回False
,新组成立,并且过程继续。
要了解为什么 groupBy
的行为,请检查其source:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
此处的关键是使用span
函数:span (1<) [2,3,2,1]
返回([2,3,2],[1])
。上面代码中的span (eq x) xs
会将xs
的所有元素与(eq x)
匹配到一对的第一部分,并将xs
的其余部分放入第二部分。 (x:ys)
然后将x
与一对的第一部分连接起来,而groupBy
以xs
的其余部分(zs
)递归调用groupBy
。这就是为什么{{1}}以这种奇怪的方式运作的原因。