2011-05-21 21 views
9

.NET框架中的LINQ庫確實有一個非常有用的函數GroupBy,我一直在使用它。 其在Haskell類型會是什麼樣子GroupBy函數來自Haskell中的.NET

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

其目的是基於給定的分類功能f項目分爲桶,與含有類似的項目每個桶,這是(b, l)使得對於l任何項目xf x == b。它在.NET中的性能是O(N),因爲它使用散列表,但在Haskell中,我確定O(N * log(N))。

我在標準的Haskell庫中找不到類似的東西。另外,我的標準功能方面實現多少有些笨重:

myGroupBy :: Ord k => (a -> k) -> [a] -> [(k, [a])] 
myGroupBy f = map toFst 
     . groupBy ((==) `on` fst) 
     . sortBy (comparing fst) 
     . map (\a -> (f a, a)) 
    where 
     toFst [email protected]((k,_):_) = (k, map snd l) 

這絕對不是我想看到我之間特定問題的代碼。

我的問題是:我怎樣才能實現這個功能很好地利用標準庫到他們最大?

此外,似乎沒有這樣的標準功能暗示它可能很少有經驗的Haskellers需要,因爲他們可能知道一些更好的方法。真的嗎?什麼可以用來以更好的方式實現類似的功能?

另外,考慮到groupBy已經採取了什麼名稱呢? :)

+1

及其在Haskell類型是'奧德B =>(A-> B) - >並[a] - > [(二,[a])]' – 2011-05-27 09:26:48

+0

噢,我的...而且沒有人注意到! – Rotsor 2011-05-27 10:59:06

回答

3

使用Data.Map作爲中間結構:

import Control.Arrow ((&&&)) 
import qualified Data.Map as M 

myGroupBy f = M.toList . M.fromListWith (++) . map (f &&& return) 

map操作接通輸入列表與含有元素單列表成對密鑰的列表。 M.fromListWith (++)將其變爲Data.Map,當兩個項目具有相同的密鑰時連接,並且M.toList將對再次取出。

請注意,這將反轉列表,因此必要時進行調整。如果例如只想要每個組中的元素的總和,則將return(++)替換爲其他類似monoid的操作也是容易的。

+0

這個工程!但是,有一點區別:它會顛倒結果列表。很好地使用箭頭順便說一句。他們甚至開始對我有意義! – Rotsor 2011-05-21 04:21:42

+0

啊,是的。你可以通過使用'flip(++)'或者僅僅使用'map(second reverse)',另一個箭頭例子來進行後處理來補救這個問題:)後者可能會更有效,因爲你避免了可能的O(n^2)列表連接。 – hammar 2011-05-21 04:27:20

+0

@Rotsor:如果沒有其他事情發生,你總是可以反轉結果列表。這也可能比Data.List版本更快,因爲「Data.Map」按照它的順序排序,而「groupBy」只有一個相等的謂詞(想想這意味着......)。 – 2011-05-21 04:28:20