2013-03-14 82 views
20

Scala的函数列表中有一个函数groupBy,它接受一个从列表项中提取关键字的函数,并返回另一个列表,其中的项目是由关键字和产生该关键字的项目列表组成的元组。换句话说,这样的事情:Haskell相当于Scala的组合

List(1,2,3,4,5,6,7,8,9).groupBy(_ % 2) 
// List((0, List(2,4,6,8)), (1, List(1,3,5,7,9))) 

(事实上,它看起来像在当前版本中,它提供了一个Map代替,但是这并不重要)。 C#有一个更有用的版本,可以让你同时映射这些值(如果你的关键函数只是提取元组的一部分,非常有用)。

Haskell有一个groupBy,但它有些不同 - 它根据一些比较函数对事情进行分组。

在我写和写之前,Haskell中是否有相当于Scala的groupBy? Hoogle没有任何我期望的签名看起来像(下面),但我可能刚刚弄错了。

Eq b => (a -> b) -> [a] -> [(b,[a])] 

回答

17

你可以比较容易地编写自己的功能,但你需要的,如果你想要一个有效的解决方案将一个OrdHashable约束的分类函数的结果。例如:

import Control.Arrow ((&&&)) 
import Data.List 
import Data.Function 

myGroupBy :: (Ord b) => (a -> b) -> [a] -> [(b, [a])] 
myGroupBy f = map (f . head &&& id) 
        . groupBy ((==) `on` f) 
        . sortBy (compare `on` f) 

> myGroupBy (`mod` 2) [1..9] 
[(0,[2,4,6,8]),(1,[1,3,5,7,9])]  

你也可以使用一个哈希表像Data.HashMap.Strict,而不是为预期的线性时间排序。

+0

我对此进行了一些修改,使C#选项可以在同一时间对值应用一个函数:'myGroupBy fg xs = map (f。head &&& g)。 groupBy((==)\'on \'f)。 sortBy(比较\'f)$ xs' – Impredicative 2013-03-15 10:53:52

+0

@Impredicative:这看起来非常有用! – 2013-03-15 10:58:30

+0

@Impredicative:'myCSharpGroupby f g xs = map(second g)$ myGroupBy f xs'也可以工作 – cheecheeo 2013-03-22 06:28:56

3

这不是List库中的函数。

你可以把它写成sortBy和groupBy的组合。

4

具体来说,有以下应该工作:

scalaGroupBy f = groupBy ((==) `on` f) . sortBy (comparing f) 

模,这并不让你的f结果各组中,但如果你真的需要它,你可以随时后期处理与

map (\xs -> (f (head xs), xs)) . scalaGroupBy f 
+0

定义的'using'函数在哪里? – 2013-03-15 10:13:40

+0

@NiklasB。好问题,Hoogle似乎没有找到它。但我发誓它曾经在那里?!就像比较f是f x <=> f y,所以使用f应该是f x == f y – Ingo 2013-03-15 10:16:52

+0

所以基本上“相等”或某物。我认为'Data.Function.on'是这些概念的泛化,因为比较= compare =和使用= =(==)' – 2013-03-15 10:18:17

1

trace置于f表明,对于长度为2或更长的任何列表中的每个元素,使用@Niklas解决方案对f进行3次评估。我冒昧地修改它,以便f仅应用于每个元素一次。无论如何,创建和销毁元组的成本是否低于多次评估f的成本(因为f可以是任意的)。

import Control.Arrow ((&&&)) 
import Data.List 
import Data.Function 

myGroupBy' :: (Ord b) => (a -> b) -> [a] -> [(b, [a])] 
myGroupBy' f = map (fst . head &&& map snd) 
        . groupBy ((==) `on` fst) 
        . sortBy (compare `on` fst) 
        . map (f &&& id) 
+0

我不喜欢那个'head' - 我想出了'foldr go [] go(k,x)(k',xs)| k == k'=(k,x:xs);去(k,x)kxs =(k,[x]):kxs',但也许这并不清楚。 – 2013-03-21 14:12:48

+0

(我知道'head'永远不会崩溃,但我更喜欢*语法*永远不会崩溃的代码,而不必考虑它) – 2013-03-21 14:13:40

+0

@BenMillwood,您的代码不会检查。我对在'group'或'groupBy'产生的子列表使用'head'有同样的问题,但现在我习惯了。 – pat 2013-03-21 15:12:57

0

该解决方案将分解和组由(FX),不管阉是排序或不

f = (`mod` (2::Int)) 

list = [1,3,4,6,8,9] :: [Int] 


myGroupBy :: Eq t => (b -> t) -> [b] -> [(t, [b])] 

myGroupBy f (z:zs) = reverse $ foldl (g f) [(f z,[z])] zs 
    where 
    -- folding function       
    g f ((tx, xs):previous) y = if (tx == ty) 
          then (tx, y:xs):previous 
          else (ty, [y]):(tx, reverse xs):previous 
     where ty = f y       

main = print $ myGroupBy f list 

结果: [(1,[1,3]),(0, [4,6,8]),(1,[9])]