代码之家  ›  专栏  ›  技术社区  ›  Alexander Rautenberg

F#返回列表中的元素对

f#
  •  4
  • Alexander Rautenberg  · 技术社区  · 14 年前

    我一直在寻找一种优雅的方法来编写一个函数,它接受一个元素列表并返回一个元组列表,其中包含所有可能的不同元素对,而不考虑顺序,即(a,b)和(b,a)应该被认为是相同的,并且只返回其中一个。

    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"
    

    它工作并返回预期结果 但它看起来很不自然。 我不需要遍历序列表达式,然后再转换回一个列表,必须有一个只涉及基本列表操作或库调用的等效解决方案。。。

    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 回复  |  直到 14 年前
        1
  •  2
  •   Brian    14 年前

    有一种方法:

    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)
    
        2
  •  4
  •   Tomas Petricek    14 年前

    我认为您的代码实际上非常接近惯用的版本。我唯一能做的改变就是 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 ] 会给 . 使用此函数后,实现原始问题会变得更容易—您可以编写:

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

    combinations

        3
  •  2
  •   IVlad    14 年前

    你好像把事情搞得太复杂了。为什么还要使用 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)