列表中的F#返回元素对

时间:2010-10-03 19:37:06

标签: f#

我一直在寻找一种优雅的方式来编写一个函数,它接受一个元素列表并返回一个元组列表,其中包含所有可能的不同元素对,不考虑顺序,即(a,b)和( b,a)应该被认为是相同的,只返回其中一个。 我确信这是一个非常标准的算法,它可能是F#文档的封面页面中的一个例子,但我找不到它,甚至没有在Internet上搜索SML或Caml。我想出的是以下内容:

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"

同样有效,但与第一个一样,考虑到相当容易的问题,它似乎不必要地涉及和复杂。我想我的问题是关于风格的问题,真的,如果有人可以为此提出双线。

3 个答案:

答案 0 :(得分: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
    | _ -> () } 

这与Brian发布的内容几乎相同。如果您想获得一个列表作为结果,那么您可以将整个事件包裹在[ ... ]而不是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

答案 1 :(得分: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)

答案 2 :(得分: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)