【发布时间】:2020-10-07 16:36:45
【问题描述】:
在 Hackage 上,我看到 groupBy's implementation 是这样的:
groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy eq (x:xs) = (x:ys) : groupBy eq zs
where (ys,zs) = span (eq x) xs
这意味着谓词eq 位于每组的任意两个元素之间。例子:
> difference_eq_1 = ((==1).) . flip (-)
> first_isnt_newline = ((/= '\n').) . const
>
> Data.List.groupBy difference_eq_1 ([1..10] ++ [11,13..21])
[[1,2],[3,4],[5,6],[7,8],[9,10],[11],[13],[15],[17],[19],[21]]
>
> Data.List.groupBy first_isnt_newline "uno\ndue\ntre"
["uno\ndue\ntre"]
如果我想对元素进行分组,以使谓词在任意一对连续元素之间成立,那么上述结果会如下所示?
[[1,2,3,4,5,6,7,8,9,10,11],[13],[15],[17],[19],[21]]
["uno\n","due\n","tre"]
我自己写的,看起来有点丑
groupBy' :: (a -> a -> Bool) -> [a] -> [[a]]
groupBy' p = foldr step []
where step elem [] = [[elem]]
step elem gs'@((g'@(prev:g)):gs)
| elem `p` prev = (elem:g'):gs
| otherwise = [elem]:gs'
所以我在徘徊,如果这样的功能已经存在,我只是没有找到它。
关于第二个用法,Data.List.groupBy first_isnt_newline,其中二元谓词基本上忽略了第二个参数并将一元谓词应用于第一个,我刚刚发现 Data.List.HT.segmentAfter unary_predicate 可以完成这项工作,其中 unary_predicate 是转发const 的输出的一元谓词的否定。换句话说Data.List.groupBy ((/= '\n').) . const === Data.List.HT.segmentAfter (=='\n')。
【问题讨论】:
-
"这意味着谓词 eq 在每个组的任何两个元素之间都成立"。严格地说 no,它由组的 first 和组的其余部分持有。
-
这个想法是你传递一个 equivalence 关系,它是 transitive,所以这意味着如果
p x y成立并且p y z成立,那么p x z也应该成立。
标签: haskell functional-programming grouping