在F#中“合并”相同长度的多个列表的惯用方法?

时间:2012-02-01 19:50:28

标签: list f# functional-programming idiomatic

我有许多列表 - 每个列表包含9个浮点数。我真正需要做的是生成一个新列表,它从我的每个列表中获取第一个元素,并将它们作为我的第一个元素添加在一起,然后将每个列表中的第二个元素添加为我的第二个元素等。

如此有效,如果我的数据看起来像这样:

List1 = [a1; b1; c1; d1; e1; f1; g1; h1; i1]
List2 = [a2; b2; c2; d2; e2; f2; g2; h2; i2]
...
Listn = [an; bn; cn; dn; en; fn; gn; hn; in]

然后我需要生成一个新列表Listx,以便

Listx = [a1 + a2 + ... + an; b1 + b2 + ... + bn; ... ]

我将要合并的列表数量会有所不同(有时我可能只有一个包含9个数字的列表,有时超过100个列表,总共9个元素长),所以我想知道是否有人对此有任何建议很好的惯用方式吗?

我确实看过this questionthis one,但两人似乎都主张先对我的元素编制索引,然后使用groupby。这让我感到困惑,因为a)我觉得可能有一个更优雅的解决方案适用于我的特定情况和b)性能可能是一个问题以后 - 我不想过早优化,但我也不想拍自己在脚下。

5 个答案:

答案 0 :(得分:7)

这是一个解决方案,它适用于长度相同的列表列表:

let mapN f = function
   | []  -> []
   | xss -> List.reduce (List.map2 f) xss

let inline merge xss = mapN (+) xss

// Usage
let yss = List.init 1000 (fun i -> [i+1..i+9])
let ys = merge yss

答案 1 :(得分:2)

我会像矩阵一样轻松过关:

let merge xss =
  let m = matrix xss
  List.map (m.Column >> Seq.sum) [0..m.NumCols-1]

答案 2 :(得分:1)

这是一种方法:

let merge lists =
  let rec impl acc lists =
    match List.head lists with
    | [] -> List.rev acc
    | _  -> let acc' = (lists |> List.map List.head |> List.reduce (+))::acc
            let lists' = List.map List.tail lists
            impl acc' lists'
  impl [] lists

一些注意事项:

  • List.reduce (+)代替List.sumList.sumBy,因为后者仅适用于数字类型,而(+)可用于例如string merge
  • int list list -> int list被推断为+类型,而不是通用的,因为运算符int的工作方式很微妙。如果您只需要它来处理单个类型,并且该类型 float(例如merge),那么向let merge (lists:float list list) = 添加类型注释将足够了:

    merge
  • inline可以标记为+,然后适用于支持运算符merge的任何类型,但如果有更多内容,这将导致IL中出现大量膨胀比一个或两个呼叫站点。如果您有多种类型需要使用merge,并且事先都知道所有类型,那么这里的一个好的解决方法是制作inline private(可能还有merge)然后定义根据通用let inline merge lists = let rec impl acc lists = match List.head lists with | [] -> List.rev acc | _ -> let acc' = (lists |> List.map List.head |> List.reduce (+))::acc let lists' = List.map List.tail lists impl acc' lists' impl [] lists let mergeInts (lists:int list list) = merge lists let mergeFloats (lists:float list list) = merge lists let mergeStrings (lists:string list list) = merge lists

    实现的不同类型特定函数
    merge

    如果你只调用特定类型的{{1}},那么IL膨胀应该可以忽略不计。

  • 最后,如果性能确实是一个问题,那么请使用数组而不是列表。

答案 3 :(得分:1)

// Shamelessly stolen from:
// http://hackage.haskell.org/packages/archive/base/latest/doc/html/src/Data-List.html#transpose
let rec transpose = function
  | [] -> []
  | ([] :: xss) -> transpose xss
  | ((x :: xs) :: xss) -> (x :: List.map List.head xss) :: transpose (xs :: List.map List.tail xss)

let fuse = transpose >> List.map List.sum

printfn "%A" (fuse [[3; 4]; [1; 90]; [34; 89]]) // prints [38; 183]

答案 4 :(得分:0)

当列表列表为空时,这里是一个具有默认值的替代单行:

let sumVertically length = List.fold (List.map2 (+)) (List.replicate length 0)

//usage
//stolen from pad's answer
let listOfLists = List.init 1000 (fun i -> [i+1..i+9])
sumVertically 9 listOfLists