F#中的小组总数 - 序列简单,是否可以使用列表?

Ser*_*hov 7 f#

给定一个组ID /值元组的序列,很容易计算组总数(与使用C#和LINQ的方式非常相似):

let items = ["g1",5; "g2",10; "g1",20]

let groupsums = 
    items  
    |> Seq.groupBy (fun x -> fst x) 
    |> Seq.map (fun (g, s) -> Seq.fold (fun acc x -> acc + snd x) 0 s)
Run Code Online (Sandbox Code Playgroud)

但是对于F#来说是新手,我看不到与列表相同的方法.我是否必须使用可变变量,或者是否有一种功能性的方法来对列表进行相同的操作?

gra*_*bot 8

没有内置的List.groupBy.许多F#内置类型具有分配了所述函数的seq版本的函数.例如来自list.fs

let inline sumBy f (list : list<_>) = Seq.sumBy f list

我很确定F#的设计师为了保持一致性以及为了DRY省略了什么而有很多关于要复制什么的讨论.我个人希望他们坚持干.

如果你想创建自己的"功能"List.group我会使用地图和列表.

let groupBy list =
    list 
    |> List.fold (fun group (g, x) -> 
        match group |> Map.tryFind g with
        | Some(s) -> group |> Map.remove g |> Map.add g (x::s)
        | None -> group |> Map.add g [x]
        ) Map.empty
    |> Map.toList 

let groupsums = groupBy >> List.map (snd >> List.sum)
Run Code Online (Sandbox Code Playgroud)

如果您只需要总和,则可以跳过保留列表.

let groupAndSumBy list =
    list 
    |> List.fold (fun group (g, x) -> 
        match group |> Map.tryFind g with
        | Some(s) -> group |> Map.remove g |> Map.add g (x + s)
        | None -> group |> Map.add g x
        ) Map.empty
    |> Map.toList
    |> List.map snd
Run Code Online (Sandbox Code Playgroud)

产量

> groupsums items;;
val it : int list = [25; 10]

> groupAndSumBy items;;
val it : int list = [25; 10]
Run Code Online (Sandbox Code Playgroud)

  • 我认为`groupAndSumBy`是最快的.只要你使用一堆内置函数来组成一个更大的函数,你就会付出一些性能.您支付这个价格以换取可读性和开发速度. (2认同)

kvb*_*kvb 7

虽然gradbot的解决方案没有任何问题,但我只是保持简单并Seq.toList在需要时将序列转换回列表.所以你可以将你的定义重写为:

let groupsums =
    items
    |> Seq.groupBy fst
    |> Seq.toList
    |> List.map (fun (_,s) -> Seq.sumBy snd s)
Run Code Online (Sandbox Code Playgroud)


Dan*_*iel 7

虽然我会使用kvb的建议,如果你打算推出自己的建议,我建议使用Dictionary而不是Map.在我的测试中它至少快了400%.

let groupBy f (list:list<_>) =
  let dict = Dictionary()
  for v in list do
    let k = f v
    match dict.TryGetValue(k) with
    | true, l -> dict.[k] <- v :: l
    | _ -> dict.Add(k, [v])
  dict |> Seq.map (|KeyValue|) |> Seq.toList
Run Code Online (Sandbox Code Playgroud)

要么:

let groupSumBy (list:list<_>) =
  let dict = Dictionary()
  for k, v in list do
    match dict.TryGetValue(k) with
    | true, n -> dict.[k] <- v + n
    | _ -> dict.Add(k, v)
  dict |> Seq.map (|KeyValue|) |> Seq.toList
Run Code Online (Sandbox Code Playgroud)

通过ref版本:

let groupSumBy (list:list<_>) =
  let dict = Dictionary()
  let mutable n = 0
  for k, v in list do
    match dict.TryGetValue(k, &n) with
    | true -> dict.[k] <- v + n
    | false -> dict.Add(k, v)
  dict |> Seq.map (|KeyValue|) |> Seq.toList
Run Code Online (Sandbox Code Playgroud)