2012-02-01 35 views
5

我有許多列表 - 每個列表包含9個浮點數。我實際上需要做的是生成一個新列表,將每個列表中的第一個元素添加爲第一個元素,然後將每個列表中的第二個元素添加爲第二個元素,等等。在F#中「合併」多個相同長度的列表的習慣用法?

So有效,如果我的數據看起來是這樣的:

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)性能可能稍後會出現問題 - 我不想過早優化,但我也不想拍攝自己在腳下。

+2

「 *表現可能稍後會有問題 - 我不想過早地優化,但我也不想在腳下拍自己。*「我同意這種觀點,但值得注意的是,如果您正確封裝如果性能是一個問題,稍後改變實現的功能應該是無痛的,不會影響其他代碼。 – ildjarn 2012-02-01 20:32:47

回答

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

您需要將'sumAll'' inline'標記爲與'float'和其他數字類型一起使用。 – Daniel 2012-02-01 20:33:06

+0

感謝您指出,修正。 – pad 2012-02-01 20:35:50

+0

完美!謝謝 – 2012-02-01 21:04:35

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和那麼將適用於支持運營商+的任何類型,但如果有多個或兩個呼叫站點,這將導致您的IL中出現大量臃腫。如果您有多個類型的需要merge工作,並且都事先知道的,然後有好的解決方法是使mergeinline(也可能是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秒,IL膨脹可以忽略不計。

  • 最後,如果性能是真的擔心,那麼使用數組而不是列表。
2

我會通過一些輕鬆類似矩陣去:

let merge xss = 
    let m = matrix xss 
    List.map (m.Column >> Seq.sum) [0..m.NumCols-1] 
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] 
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 
相關問題