2

大きなリスト (float*float) のルックアップ関数を作成する必要があります。この関数は、キーが見つからない場合は新しいエントリを追加し、キーが見つかった場合は値を合計します。メモ化された計算について読んだことがありますが、実際にはそれほど難しくありませんでした。ここに私が持っているものがあります:

let memoLookUp basearr lookarr =
    let t = new System.Collections.Generic.Dictionary<float,float>()
    for (a,b) in basearr do
        t.Add(a,b)
    for (a, b) in lookarr do
        if t.ContainsKey(a) then t.[a] <- t.[a] + b
        else t.Add(a,b)
    t

サンプルデータ:

let basearr = [(41554., 10.0) ; (41555., 11.0) ; (41556., 12.0) ; (41557., 10.0) ; (41558., 13.0) ]

let lookarr = [(41555., 14.0) ; (41556., 15.0) ; (41559., 16.0)]

これは期待どおりに返されます。

私の質問は次のとおりです。

  • リストが長い場合 (たとえば、それぞれ約 30000)、パフォーマンスの観点からこのようにすることは賢明ですか?
  • それとも、(各データ リストの列 1 で) 日付で並べ替えてから、より命令的なアプローチを使用する方がよいでしょうか?
  • それとも、f# または c# に sth ビルドさえありますか?
4

2 に答える 2

4

既存のコードで 2 つの配列をマージすると、より均一な動作になる場合があります。特に必要でない限り (たとえば、basearr に重複が含まれている場合にプログラムをクラッシュさせたい場合)、uniform の方が優れています。

let incrementalAdderImperative aseq = 
  let d= System.Collections.Generic.Dictionary<_,_>()
  Seq.iter(fun (k,v) ->  if d.ContainsKey(k) 
                         then d.[k] <- d.[k] + v
                         else d.Add(k,v)) aseq

あなたの質問に答えるには:

  • リストが長い場合 (たとえば、それぞれ約 30000)、パフォーマンスの観点からこのようにすることは賢明ですか?

Dictionary クラスに依存して、ハッシュベースの辞書を使用しています。したがって、まったく劣化しないはずです。これは、IDictionary で説明されている辞書の機能ではなく、辞書のこの実装のプロパティであることに注意してください。他の実装があります (たとえば Map)

パフォーマンスが気になる場合は、内部のサイズ変更を避けるために、発生するキーの数を (高速に) 推定して辞書を初期化する必要があります。使用されている具体的なタイプを知っている(ハッシュベースの辞書など)

  • (各データ リストの列 1 で) 日付で並べ替えてから、より命令的なアプローチを使用する方がよいでしょうか?

日付順にソートすると、折りたたむことができます。これはもっと速いと思いますが、あなたが言及した数はそれほど大きくありません。

let oneshotAdder reducer kvArr =
    kvArr |> Array.sortInPlaceBy fst
    let a = kvArr 
            |> Array.fold(fun (res) (k,v) ->  
                            match res with
                            | []                             -> (k,v)::res
                            | ((prevk,_)::xs) when k = prevk -> (k,reducer v (List.head res |> snd))::(List.tail res)
                            | _                              -> (k,v)::res)
                          List.empty
    dict a
let data = Array.concat ([basearr; lookarr] |> List.map List.toArray)
let dict2 = oneshotAdder (+) data

ps : あなたが与えた例では、 basearr と lookarr は配列ではなくリストであるため、実際に配列を操作したいと仮定すると無関係な操作になります。

  • f# または c# には sth ビルドさえありますか?

F# では、groupby をネイティブに実行して要素を合計できます。コレクション変換の本質は、関数を渡すことなので、それをネイティブに持っていても不思議ではありません。C# では、Linq を使用してそのような列挙変換を取得できます。これは、内部では fsharp のようないくつかの関数にマップされます。

let groupByAdder reducer (kvArr:('k*'v) array)  =
    kvArr |> Seq.groupBy fst 
          |> Seq.map (fun (k,vs) -> k , vs |> Seq.map snd |> (Seq.reduce reducer)) 
          |> dict
let dict3 = groupByAdder (+) data 
于 2013-10-10T13:39:20.463 に答える
1

私はするだろう:

Seq.groupBy fst kvs
|> Seq.map (fun (k, vs) -> k, Seq.map snd vs |> Seq.reduce (+))
|> dict
于 2013-10-11T01:32:37.017 に答える