我有一个这样的数组:
let items = ["A";"B";"C";"D"]
我想把它转换成这样的数组:
let result = ["AB";"AC";"AD";"BC";"BD";"CD"]
我在语言规范中找不到任何内容 - 尽管我可能搜索不正确。我想到了Seq.Fold这样:
let result = items |> Seq.fold(fun acc x -> acc+x) ""
但我得到了"ABCD"
有谁知道怎么做?修改后的CartesianProduct会有效吗?
提前致谢
答案 0 :(得分:3)
你有的是列表,而不是数组 - 列表使用[...]
语法,数组使用[|...|]
语法。
那就是说,这是一个简单的实现:
let listProduct (items : string list) =
items
|> List.collect (fun x ->
items
|> List.choose (fun y ->
if x < y then Some (x + y)
else None))
如果你把它放入F#interactive:
> let items = ["A"; "B"; "C"; "D"];;
val items : string list = ["A"; "B"; "C"; "D"]
> items |> listProduct |> Seq.toList;;
val it : string list = ["AB"; "AC"; "AD"; "BC"; "BD"; "CD"]
答案 1 :(得分:2)
这样的事情应该这样做:
items
|> List.map (fun x -> items |> List.map (fun y -> (x, y)))
|> List.concat
|> List.filter (fun (x, y) -> x < y)
|> List.map (fun (x, y) -> x + y)
|> List.sort
我不知道它是否对大型列表有效,但它确实产生了这个输出:
["AB"; "AC"; "AD"; "BC"; "BD"; "CD"]
<强>击穿强>
第一步通过两次映射items
生成元组列表列表:
[[("A", "A"); ("A", "B"); ("A", "C"); ("A", "D")];
[("B", "A"); ("B", "B"); ("B", "C"); ("B", "D")];
[("C", "A"); ("C", "B"); ("C", "C"); ("C", "D")];
[("D", "A"); ("D", "B"); ("D", "C"); ("D", "D")]]
其次,List.concat
将列表列表转换为单个列表:
[("A", "A"); ("A", "B"); ("A", "C"); ("A", "D"); ("B", "A"); ("B", "B");
("B", "C"); ("B", "D"); ("C", "A"); ("C", "B"); ("C", "C"); ("C", "D");
("D", "A"); ("D", "B"); ("D", "C"); ("D", "D")]
第三,List.filter
删除第一个元素等于或大于第二个元素的元组:
[("A", "B"); ("A", "C"); ("A", "D"); ("B", "C"); ("B", "D"); ("C", "D")]
第四,List.map
生成一个连接字符串列表:
["AB"; "AC"; "AD"; "BC"; "BD"; "CD"]
最后,List.sort
对列表进行排序,但在这种情况下没有必要,因为列表已经有正确的顺序。
您可能还会考虑使用Seq.distinct
删除重复项(如果有的话)。
答案 2 :(得分:1)
您可以创建一个函数来创建列表中所有头/尾对的列表:
let rec dec = function
| [] -> []
| (x::xs) -> (x, xs) :: dec xs
或尾递归版:
let dec l =
let rec aux acc = function
| [] -> acc
| (x::xs) -> aux ((x, xs)::acc) xs
aux [] l |> List.rev
然后您可以使用此功能创建列表:
let strs (l: string list) = l |> dec |> List.collect (fun (h, t) -> List.map ((+)h) t)
答案 3 :(得分:1)
我会这样做:
let rec loop = function
[] -> []
| x :: xs -> List.map ((^) x) xs @ loop xs
这样做的好处是不会从列表中构建每对元素只丢弃一半。 (我将把这个附加物除掉作为练习: - )
对我而言,与其他一些提议的解决方案相比,它更容易分辨出这里发生了什么。对于这类问题,在处理元素x的位置,您还需要访问列表xs的其余部分,标准组合器并不总能使解决方案更清晰。
答案 4 :(得分:1)
let items = ["A";"B";"C";"D"]
let rec produce (l: string list) =
match l with
// if current list is empty or contains one element - return empty list
| [] | [_] -> []
// if current list is not empty - match x to head and xs to tail
| x::xs ->
[
// (1)
// iterate over the tail, return string concatenation of head and every item in tail
for c in xs -> x + c
// apply produce to tail, concat return values
yield! produce xs
]
第一次迭代:l = [A,B,C,D] - 不为空,在第二次匹配的情况下,我们将得到x = A,xs = [B,C,D]。 'for'部分列表表达式将产生[AB,AC,AD]以及将produce
应用于xs的结果。
第二次迭代:l = [B,C,D]不为空所以第二次匹配的情况我们将x = B,xs = [C,D]。 'for'部分列表表达式将产生[BC,BD]和将produce
应用于xs的结果。
第3次迭代:l = [C,D]在第二次匹配情况下不为空,我们将x = C,xs = [D]。 'for'部分列表表达式将产生[CD]和将produce
应用于xs的结果。
第4次迭代:l = [D]包含一个元素 - &gt;返回空列表。
最终结果将是[AB,AC,AD] ++ [BC,BD] ++ [CD]
的串联答案 5 :(得分:1)
这是在F#中实现List monad的一个恰当的激励示例。使用F#计算表达式,我们得到:
type ListMonadBuilder() =
member b.Bind(xs, f) = List.collect f xs
member b.Delay(f) = fun () -> f()
member b.Let(x, f) = f x
member b.Return(x) = [x]
member b.Zero() = []
let listM = new ListMonadBuilder()
现在,为了解决原始问题,我们只需使用List monad。
let run = listM {
let! x = ['A' .. 'D']
let! y = List.tail [ x .. 'D']
return string x + string y
}
<#> run();;
在F#Interactive中将返回所需的结果。
关于使用List monad的另一个例子,我们可以获得Pythagorean三元组<= n
。
let pythagoreanTriples n = listM {
let! c = [1 .. n]
let! b = [1 .. c]
let! a = [1 .. b]
if a*a + b*b = c*c then return (a, b, c)
}
在F#interactive中运行pythagoreanTriples 10 ();;
返回:
val it : (int * int * int) list = [(3, 4, 5); (6, 8, 10)]