我让我的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#中编写此代码?
答案 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 []