2014-03-25 1 views
1

첫 번째 멤버가 이름 인 쌍의 목록을 취하는 pointCounts 함수를 정의하고 두 번째로 점수 값을 계산하고 각 이름에 대해 카운트 된 포인트가있는 쌍의 목록을 반환합니다.Haskell : fst로 필터링하고 쌍의 snd로 계산합니다.

저는 며칠 동안이 문제로 고생하고 있지만, 어떻게해야하는지 잘 모르겠습니다.

pointCount [("Ferp",25),("Herp",18),("Derp",15),("Ferp",25),("Herp",15),("Derp",18),("Jon",10)]

원하는 출력 예 : I가 중간 데이터 구조로 Data.Map 사용

[("Ferp",50),("Herp",33),("Derp",33),("Jon",10)] 
+0

[전날 비슷한 문제가있는 사람을 도왔습니다] (http://stackoverflow.com/questions/22580319/f1-results-with-haskell/22580945#22580945). 이 질문은 중복되는 것으로 생각하지 않지만 같은 문제가 해결되고 있습니다. – bheklilr

답변

1

다음은 목록 이외의 다른 것을 사용하는 또 다른 해결책입니다. 그러나 cdk의 솔루션은 더 간단하고 더 나은 실행 시간을 가지고 있습니다. 여기

import Data.List 

pointCount :: Num a => [(String, a)] -> [(String, a)] 
pointCount [] = [] 
pointCount ((x, n):xs) = 
    let (eqx, neqx) = partition ((==x).fst) xs in 
     (x, n + (sum$ map snd eqx)) : pointCount neqx 
6

:

import qualified Data.Map as M 

pointCount :: Num a => [(String, a)] -> [(String, a)] 
pointCount = M.toList . foldr f M.empty 
    where f (name, val) = M.insertWith (+) name val 
      -- or pointfree 
      -- f = uncurry (M.insertWith (+)) 

또는

imput의 exmaple는 같아야 더 나은 (Daniel Wagner가 지적한대로)

pointCount = M.toList . M.fromListWith (+) 
+0

이름이 길면 해시 값을 지불하거나 trie 등을 사용할 수 있습니다. – dfeuer

+1

@ dfeuer (또는 오히려 어떤 "또는 무언가"가 무엇인지 모르는 사람들). 'Data.HashMap'이 존재하며 그러한 경우에 유용합니다. 두 개의 구현체가 존재한다.'hashmap'과'unordered-containers' 패키지를 보라. –

+1

'critbit' 패키지를리스트에 추가합니다.이 패키지는'ByteString' /'Text' 키를 효율적으로 색인하기 위해 고안되었습니다. 성능은 해시 맵과 거의 동일하지만 해시 맵이 제공 할 수없는 편리한 기능을 제공합니다 (물론이 답변에는 필요하지 않습니다). – cdk

1

내가 결과의 순서는 중요하지 않습니다 가정, 그것을 할 것입니다 방법 및 그것은 단지 작은 목록 (즉, 효율성이 정말 중요하지 않습니다)에 사용되는 :

import   Data.Ord  (comparing) 
import   Data.Function (on) 
import   Data.List  (groupBy, sortBy, foldl1') 

pointCount :: Num a => [(String, a)] -> [(String, a)] 
pointCount = map  (foldl1' sumSecond) 
      . groupBy ((==) `on` fst) 
      . sortBy (comparing fst) 
    where 
    sumSecond :: Num a => (String, a) -> (String, a) -> (String, a) 
    sumSecond (_, accum) (name, v) = (name, accum + v) 

합계의 세미 그룹 특성을 이용하여 비어 있지 않은 목록의 첫 번째 항목과 세미 그룹 쌍을 찾는 또 다른 가능한 (유사한) 솔루션이 있습니다. 또한 두 개의 세미 그룹을 작성할 때 세미 그룹을 얻습니다 (semigroupssemigroupoids 패키지) :

import   Data.Ord     (comparing) 
import   Data.Function    (on) 
import   Data.List     (groupBy, sortBy) 
import   Data.Semigroup   (First (..), Sum (..)) 
import   Data.Semigroup.Foldable (foldMap1) 
import qualified Data.List.NonEmpty as NE 
import   Control.Arrow    ((***)) 

pointCount :: Num a => [(String, a)] -> [(String, a)] 
pointCount = map  (unpackResult 
         . foldMap1 packSemigroup 
         . NE.fromList 
         ) 
      . groupBy ((==) `on` fst) 
      . sortBy (comparing fst) 
    where 
    packSemigroup :: Num a => (String, a) -> (First String, Sum a) 
    packSemigroup = First *** Sum 

    unpackResult :: Num a => (First String, Sum a) -> (String, a) 
    unpackResult = getFirst *** getSum 

아마도 첫 번째 해결 방법을 사용 하겠지만 두 번째 방법은 문제의 본질을 세미 그룹 구성에서 어떻게 처리 할 수 ​​있는지 보여줍니다. 이 작업은 구체적으로 반자족 동형이면서 unpackResult . foldMap1 packSemigroup 부분으로 표시됩니다.