groupBy的Lambda的争论

时间:2014-05-15 03:03:04

标签: haskell

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个参数的含义是什么:xy

5 个答案:

答案 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函数有两个名为xy的参数,就像任何其他函数声明一样。

查看此表达式的最简单方法是运行它:

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)

在这样的情况下,最好直接前往源头! groupByData.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

首先,顶部的文档行告诉我们groupBygroup的非重载版本,这是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是“当前”组的第一个元素,ysomeList的元素。 groupBy遍历someList,因此每一步y都成为someList的下一个元素。当谓词返回False时,新组启动。 y成为新组的第一个成员。迭代继续,此新组的第一个成员现在变为xsomeList的下一个元素变为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. 将1与2.1进行比较。 2,因此两个数字都进入同一组。小组:[[1,2]]
  2. 将1与3.1进行比较。 3,因此3进入旧组。小组:[[1,2,3]]
  3. 将1与2.1进行比较。 2,因此2进入旧组。小组:[[1,2,3,2]]
  4. 将1与1. 1≮1进行比较,从而形成新组,并将1作为其第一个元素。小组:[[1,2,3,2],[1]]
  5. 练习:要了解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与一对的第一部分连接起来,而groupByxs的其余部分(zs)递归调用groupBy。这就是为什么{{1}}以这种奇怪的方式运作的原因。