2010-10-03 102 views
4

我一直在尋找一種優雅的方式來編寫一個函數,該函數接受元素列表並返回包含所有可能的不同元素對的元組列表,而不考慮順序,即( a,b)和(b,a)應該被認爲是相同的,只有其中一個被退回。 我相信這是一個非常標準的算法,它可能是來自F#文檔封面的一個例子,但我找不到它,甚至沒有在Internet上搜索SML或Caml。我想出瞭如下:列表中的F#返回元素對

let test = [1;2;3;4;5;6] 

let rec pairs l = 
    seq { 
     match l with 
     | h::t -> 
      yield! t |> Seq.map (fun elem -> (h, elem)) 
      yield! t |> pairs 
     | _ ->() 
     } 

test |> pairs |> Seq.toList |> printfn "%A" 

這工作,並返回預期的結果[(1,2); (1,3); (1,4); (1,5); (1,6); (2,3); (2,4); (2,5); (2,6); (3,4); (3,5); (3,6); (4,5); (4,6); (5,6)]但它看起來非常單一。 我不應該需要經過序列的表達,然後再轉換回一個列表,必須只有兩項基本列表操作或庫調用等效的解決方案...

編輯:

我也有這個在這裏

let test = [1;2;3;4;5;6] 

let rec pairs2 l = 
    let rec p h t = 
     match t with 
     | hx::tx -> (h, hx)::p h tx 
     | _ -> [] 
    match l with 
    | h::t -> p h t @ pairs2 t 
    | _ -> [] 


test |> pairs2 |> Seq.toList |> printfn "%A" 

同樣工作,但像第一個似乎不必要地介入和複雜,給予相當容易的問題。我想我的問題是關於風格的,真的,如果有人能爲此提出雙線。

+0

哎呀,人們已經發布sugegstions,而我正在做我的編輯。謝謝你們,你們都這麼快! – 2010-10-03 19:56:38

回答

2

這裏有一種方法:

let rec pairs l = 
    match l with 
    | [] | [_] -> [] 
    | h :: t -> 
     [for x in t do 
      yield h,x 
     yield! pairs t] 
let test = [1;2;3;4;5;6] 
printfn "%A" (pairs test) 
+0

雖然'|我不需要前兩個匹配模式[] | [_] - > []',但可以在最後添加一個全接觸,對吧? ('| _ - > []') – 2010-10-03 20:37:14

+0

沒錯。 (更多評論長度的文字) – Brian 2010-10-04 05:34:20

2

你似乎很複雜的事情。爲什麼即使使用seq如果你想要一個列表?如何

let rec pairs lst = 
    match lst with 
    | [] -> [] 
    | h::t -> List.map (fun elem -> (h, elem)) t @ pairs t 


let _ = 
    let test = [1;2;3;4;5;6] 
    printfn "%A" (pairs test) 
+1

我不是100%確定的,但我認爲使用序列表達式比使用'@'連接列表更有效率。 – 2010-10-03 19:51:28

4

我認爲你的代碼實際上是非常接近的慣用版本。我要做的唯一改變是我將在序列表達式中使用for,而不是將yield!Seq.map一起使用。我也常常不同格式的代碼(但是這只是一個個人喜好),所以我會寫:

let rec pairs l = seq { 
    match l with 
    | h::t -> for e in t do yield h, elem 
       yield! pairs t 
    | _ ->() } 

這幾乎是同樣的事情,什麼布萊恩公佈。如果你想得到一個清單作爲結果,那麼你可以包裝整個事情在[ ... ]而不是seq { ... }

但是,實際上並非如此 - 在封面下,編譯器無論如何都使用了一個序列,它只是將轉換添加到列表中。我認爲在實際需要列表之前使用序列可能是一個好主意(因爲序列是以拉齊爾來評估的,所以你可以避免評估不必要的東西)。

如果您希望通過將部分行爲抽象爲單獨的(通常有用的)函數來使其更簡單一些,那麼您可以編寫一個函數,例如splits返回一個列表中的所有元素與所述列表的其餘部分一起:

let splits list = 
    let rec splitsAux acc list = 
    match list with 
    | x::xs -> splitsAux ((x, xs)::acc) xs 
    | _ -> acc |> List.rev 
    splitsAux [] list 

例如splits [ 1 .. 3 ]會給[(1,[2; 3]); (2,[3]); (3,[])]。當你有這樣的功能,實現您原來的問題變得更加容易 - 你可以這樣寫:

[ for x, xs in splits [ 1 .. 5] do 
    for y in xs do yield x, y ] 

至於谷歌上搜索指南 - 這個問題被稱爲從給定的一組發現所有2元combinations

+0

謝謝,從答案中學到了一些東西。 – 2010-10-03 20:26:47

+0

rec對不是尾遞歸,是嗎? – 2016-04-21 16:19:18