2

私はこれを解決しようとしてきましたが、私はそれを理解することができません。だから、私はタプルのリストを持っています、例えば:

[("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]

そして、私が取得したいのは、タプルも含むリストです。名前が同じ場合は、それらのタプルの番号を追加する必要があります。そうでない場合は、そのタプルも最終リストの一部である必要があります。

[("Mary",25), ("John", 55), ("Bradley", 30)]

自分のことをよく説明したかどうかはわかりませんが、例を参考にしていただければと思います。

私はこれを試しましたが、機能しません:

test ((a,b):[]) = [(a,b)]
test ((a,b):(c,d):xs) | a == c = (a,b+d):test((a,b):xs)
                      | otherwise = (c,d):test((a,b):xs)
4

4 に答える 4

8

この種のことを行うことは、リストのシーケンシャルな性質のために常に厄介です。リストは、「一致するアイテムの検索」や「リスト要素の特定の組み合わせを組み合わせて新しいリストを計算する」などの操作にはあまり適していません。それは本質的に非シーケンシャルです。

少し前に戻った場合、ここで本当にやりたいことはString、リスト内の個別の番号ごとに、それに関連付けられているすべての番号を見つけて合計することです。これはData.Map、Haskellの最も標準的なものがにあるKey-Valueスタイルのデータ構造に適しているように聞こえます。これにより、任意の値タイプと任意の順序付きキータイプ(つまり、のインスタンスOrd)のKey-Valueマップが得られます。

したがって、リストからを構築するには、...で関数をMap使用できます。この関数は、便利なことに、Key-Valueタプルのリストの形式で入力を期待します。だからあなたはこれを行うことができます...fromListData.Map

import qualified Data.Map as M

nameMap = M.fromList [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]

...しかし、それは良くありません。なぜなら、それらを直接挿入すると、数字を追加するのではなく上書きするからです。重複キーを挿入するときに値を組み合わせる方法を指定するために使用M.fromListWithできます。通常、これを使用して、各キーの値のリストなどを作成するのが一般的です。

しかし、あなたの場合、目的の結果に直接スキップできます。

nameMap = M.fromListWith (+) [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]

これにより、新しい名前が見つかった場合は直接挿入されます。それ以外の場合は、重複する値(数値)が追加されます。必要に応じて、次を使用してタプルのリストに戻すことができますM.toList

namesList = M.toList $ M.fromListWith (+) [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]

これにより、の最終結果が得られ[("Bradley",30),("John",55),("Mary",25)]ます。

しかし、名前/番号のコレクションでより多くのことをしたい場合は、完了するMapまでそれを保持する方が理にかなっているかもしれません。

于 2012-12-30T23:09:33.967 に答える
4

リストを使用する別の方法は次のとおりです。

import Data.List

answer :: [(String, Int)] -> [(String, Int)]
answer = map (foo . unzip) . groupBy (\x y -> fst x == fst y) . sort            
   where foo (names, vals) = (head names, sum vals)

これはかなり簡単なアプローチです。まず、ドット(.)は関数の合成を表します。これにより、ある関数から次の関数に値を渡すことができます。つまり、ある関数の出力が次の関数の入力になります。sortまず、リスト内で名前を自動的に並べて移動するアプリケーションを適用します。次にgroupBy、類似した名前の各ペアを1つのリストに入れるために使用します。最終的にリストのリストが作成され、それぞれに類似した名前のペアが含まれます。

[[("Bradley",30)], [("John",10),("John",45)], [("Mary",10),("Mary", 15)]]

そのようなリストを考えると、各サブリストをどのように処理しますか?つまり、すべて同じ名前を含むリストをどのように処理しますか?

明らかに、名前と値の合計を含む単一のペアにそれらを縮小したいと思います。これを実現するために、関数を選択しました(foo . unzip)が、他にも多くの方法があります。 unzipペアのリストを取得し、単一のペアを作成します。ペアには2つのリストが含まれ、最初のリストにはすべての名前が含まれ、2番目のリストにはすべての値が含まれます。fooこのペアは、前述のように、関数合成によって 渡されます。fooパターンを使用してそれを分離し、次に名前に適用headし、単一の名前のみを返し(それらはすべて同じです)、sum値のリストに適用します。sum当然、リスト内の値を合計するもう1つの標準リスト関数です。

ただし、これ(foo . unzip)はペアの単一のリストにのみ適用されますが、リストのリストがあります。ここで、関数がリスト内の各リスト、より一般的にはリスト内の各要素に適用されますmap。 最終的に、各サブリストに適用した結果を含むリストが作成されます。map(foo . unzip)(foo . unzip)

で使用されているすべてのリスト関数を確認することをお勧めしData.Listます。

于 2012-12-31T00:25:23.230 に答える
1

考えられる解決策が機能しなかった理由は、リスト内の同じキーで要素が順番に発生した場合にのみ要素をグループ化するためだと思います。その代わりに、マップ(他の言語を使用している場合は辞書と呼ばれることが多い)を使用して、表示したキーを記憶し、合計を保持します。まず、必要な関数をインポートする必要があります。

import Data.Map hiding (foldl, foldl', foldr)
import Data.List (foldl')

これで、リストに沿って折りたたむことができ、キーと値のペアごとに、それに応じてマップを更新します。

sumGroups :: (Ord k, Num n) => [(k, n)] -> Map k n
sumGroups list = foldl' (\m (k, n) -> alter (Just . maybe n (+ n)) k m) empty list

したがって、foldl'は関数を使用してリストに沿って歩きます。各要素(ここではペア(k、n))と、別の引数であるアキュムレータを使用して関数を呼び出します。これは私たちの地図で、最初は空です。要素ごとに、多分n->多分nの関数を使用してマップを変更します。これは、マップのキーkの下にまだ何も含まれていない可能性があるという事実を反映しているため、両方のケースに対処します。前の値がない場合はnを返すだけです。それ以外の場合は、前の値にnを追加します。これにより、グループの合計を含むマップが最後に表示されます。結果に対してtoList関数を呼び出すと、必要なリストが得られます。

これをghciでテストすると、次のようになります。

 $ ghci
GHCi, version 7.6.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> import Data.Map hiding (foldl, foldl', foldr)
Prelude Data.Map> import Data.List (foldl')
Prelude Data.Map Data.List> let sumGroups list = foldl' (\m (k, n) -> alter (Just . maybe n (+ n)) k m) empty list
Loading package array-0.4.0.1 ... linking ... done.
Loading package deepseq-1.3.0.1 ... linking ... done.
Loading package containers-0.5.0.0 ... linking ... done.
Prelude Data.Map Data.List> toList $ sumGroups $ [("Mary", 10), ("John", 45), ("Bradley", 30), ("Mary", 15), ("John", 10)]
[("Bradley",30),("John",55),("Mary",25)]
Prelude Data.Map Data.List> 

内部マップはバイナリツリーの形式を使用するため、グループはボーナスとして並べ替えられた順序で表示されます。したがって、順番にトラバースして並べ替えられた(とにかくキーで並べ替えられた)リストを出力するのは比較的簡単です。

于 2012-12-30T23:14:36.587 に答える
0

これが私の2セントです。Haskellプレリュードだけを使用します。

test tup = sumAll
  where
    collect ys [] = ys
    collect ys (x:xs) =
        if (fst x) `notElem` ys
        then collect (fst x : ys) xs
        else collect ys xs
    collectAllNames = collect [] tup

    sumOne [] n x = (x, n)
    sumOne (y:ys) n x =
        if fst y == x
        then sumOne ys (n + snd y) x
        else sumOne ys n x

    sumAll = map (sumOne tup 0) collectAllNames

このメソッドは、元のリストを数回トラバースします。Collectは、名前の繰り返しをスキップして、名前だけを保持する一時リストを作成します。sumOneは名前を取得し、リスト内のどの名前が一致するかを確認し、それらの番号を追加します。名前と合計を返します。

于 2012-12-31T12:10:27.640 に答える