如何在F#中将整数列表转换为这些整数的矩阵

时间:2017-11-20 20:39:49

标签: f#

我让我的F#湿透了,并且很难理解简单的概念。请帮我解决这个问题,而不是做作业!

说我有一个列表[1;2;3;4]

如何将其转换为列表元素的所有可能组合的列表:

[(1,2);(1,3);(1,4);(2,3);(2,4);(3,4)]

我想出了什么:

 let tuples=
    let lst=[1;2;3;4]
    List.map (fun i->(i,i)) lst

显然这会给出错误的结果,但如何继续?我是否只需要使用嵌套for循环方法,我将在C#中编写此代码?

4 个答案:

答案 0 :(得分:5)

你可以使用递归来实现这一点,我相信这被称为“利弊模式”

open NUnit.Framework
open Swensen.Unquote

let convert input =
    let rec loop remain acc =
        match remain with
        | x :: xs -> 
            let combos = xs |> List.map (fun i -> (x,i))
            loop xs (acc@combos)
        | x -> acc

    loop input []

[<Test>]
let TheTest () =
    let actual = convert [1;2;3;4]
    let expected = [(1,2);(1,3);(1,4);(2,3);(2,4);(3,4)]
    test <@ expected = actual @>

答案 1 :(得分:2)

一般的想法是这样的:对于列表的每个元素x,通过将其与之后的每个元素x组合,生成y - 组合的列表 x在列表中。

要做到这一点,您需要为元素编制索引,以便您可以根据彼此之前/之后的比较来比较它们。为此,您可以使用List.indexed功能:

let lst = [1;2;3;4]
let indexedLst = List.indexed lst
// indexedLst = [(0,1); (1,2); (2,3); (3,4)]

在我们对元素建立索引之后,我们可以选择一个元素(连同其索引)并生成该元素与其后的每个其他元素的组合列表:

let combineWithSuccessors (x_idx, x) = 
   [ for y_idx, y in indexedLst do 
       if y_idx > x_idx then yield (x, y) ]

// Or if you prefer not using list comprehensions:
let combineWithSuccessors (x_idx, x) = 
   indexedLst 
   |> List.filter (fun (y_idx, _) -> y_idx > x_idx)
   |> List.map (fun (_, y) -> (x, y))

// test it out:
combineWithSuccessors (2, 3) == [(3,4)]
combineWithSuccessors (0, 1) == [(1,2); (2,3); (3,4)]

最后,为每个x生成这样的组合列表,并将所有这些列表连接成一个:

let allCombinations = 
   indexedLst |> List.map combineWithSuccessors |> List.concat

然后把它们放在一起:

let allCombinations lst =
    let indexedLst = List.indexed lst

    let combineWithSuccessors (x_idx, x) = 
        [ for y_idx, y in indexedLst do 
            if y_idx > x_idx then yield (x, y) ]

    indexedLst |> List.map combineWithSuccessors |> List.concat

测试出来:

allCombinations [1;2;3;4;5]
> [(1, 2); (1, 3); (1, 4); (1, 5); (2, 3); (2, 4); (2, 5); (3, 4); (3, 5); (4, 5)]

allCombinations [5;7;1;2;3]
> [(5, 7); (5, 1); (5, 2); (5, 3); (7, 1); (7, 2); (7, 3); (1, 2); (1, 3); (2, 3)]

答案 2 :(得分:1)

以下是使用库函数的一种解决方案:

let tuples xs =
    let ys = List.distinct xs
    ys
    |> List.allPairs ys
    |> List.filter (fun (x, y) -> x < y)
[1..4] |> tuples
// val it : (int * int) list = [(1, 2); (1, 3); (1, 4); (2, 3); (2, 4); (3, 4)]

以下是基于列表推导的另一种解决方案:

let tuples' (xs: 'T list) =
    let ys = List.distinct xs
    let len1 = ys.Length - 1
    [for i in 0 .. len1 do for j in (i+1) .. len1 do yield (ys.[i], ys.[j])]
[1..4] |> tuples'
val it : (int * int) list = [(1, 2); (1, 3); (1, 4); (2, 3); (2, 4); (3, 4)]

(需要类型注释)

答案 3 :(得分:1)

为同样的问题冷却了很多解决方案,猜猜我会分享我的

let combineInts (ar : int list) = 
    let rec combine head (tail: int list) (remain: int list) =
        if tail.Length > 1 then
            (head, List.head tail) :: (combine head tail.Tail remain)
        else if remain.Length > 1 then
            (head, tail.Head) :: combine remain.Head remain.Tail remain.Tail
        else
            [(head, tail.Head)] 
    combine ar.Head ar.Tail ar.Tail

不是

的忠实粉丝
(head, List.head tail) :: combine head tail.Tail remain

所以

let combineInts (ar : int list) = 
    let rec combine head (tail: int list) (remain: int list) (combinedSoFar: (int*int) list) =
        if tail.Length > 1 then
            combine head tail.Tail remain (combinedSoFar @ [(head, tail.Head)])
        else if remain.Length > 1 then
            combine remain.Head remain.Tail remain.Tail (combinedSoFar @ [(head, tail.Head)])
        else
            combinedSoFar @ [(head, tail.Head)]
    combine ar.Head ar.Tail ar.Tail []