我需要为大型列表编写查找函数(float * float)。如果未找到密钥,则此函数应添加新条目;如果找到密钥,则应添加总和值。我已经阅读了关于memoized计算的内容,实际上并不是很难做到的。这就是我所拥有的:
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)]
按预期返回。
我的问题是:
答案 0 :(得分:4)
您现有的代码可能会合并两个数组,以实现更统一的行为。除非另有要求,(例如, 想要 如果basearr包含重复,程序会崩溃)制服更好
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)
如果您担心性能问题,则应该使用(快速)估计将会发生多少密钥来初始化字典,以避免内部调整大小。并了解所使用的具体类型(如基于哈希的字典等)。
最好按日期排序(在每个数据的第一列中) 列表),然后使用更迫切的方法?
如果按日期排序,则可以进行折叠。我认为这会更快,但你提到的数字并不是那么大。
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#中都有内置?
在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
答案 1 :(得分:1)
我愿意:
Seq.groupBy fst kvs
|> Seq.map (fun (k, vs) -> k, Seq.map snd vs |> Seq.reduce (+))
|> dict