2013-03-14 85 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])]