我最近一直在玩F#。我想知道而不是使用for循环来生成一个元素序列,它与列表中的每个其他元素相乘,我如何使用Seq map函数或类似的东西来生成类似下面的内容。
因此,例如我有一个列表[1..10]我想应用一个产生类似
的结果的乐趣[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]
我怎样才能实现这一目标?。
非常感谢你的帮助。
答案 0 :(得分:6)
let list = [1..10]
list |> List.map (fun v1 -> List.map (fun v2 -> (v1*v2)) list) |> List.collect id
最后的List.collect会使列表列表变平。 如果你想要一个懒惰的序列,它与Seq而不是List一样。
或者,使用collect
作为主迭代器,如cfern建议和obsessivley消除匿名函数:
let flip f x y = f y x
let list = [1..10]
list |> List.collect ((*) >> ((flip List.map) list))
答案 1 :(得分:5)
列表理解是最简单的方法:
let allpairs L =
[for x in L do
for y in L -> (x*y)]
或者,不使用任何循环:
let pairs2 L = L |> List.collect (fun x -> L |> List.map (fun y -> (x*y)))
编辑以回复评论:
您可以将自交叉扩展方法添加到如下列表中:
type Microsoft.FSharp.Collections.List<'a> with
member L.cross f =
[for x in L do
for y in L -> f x y]
示例:
> [1;2;3].cross (fun x y -> (x,y));;
val it : (int * int) list =
[(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
我不会在F#中使用扩展方法,感觉有点C#'ish。但这主要是因为我觉得F#中不需要流利的语法,因为我通常将我的函数与管道(|&gt;)运算符链接在一起。
我的方法是使用交叉函数扩展List模块,而不是类型本身:
module List =
let cross f L1 L2 =
[for x in L1 do
for y in L2 -> f x y]
如果这样做,您可以像使用List的任何其他方法一样使用交叉方法:
> List.cross (fun x y -> (x,y)) [1;2;3] [1;2;3];;
val it : (int * int) list =
[(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
> List.cross (*) [1;2;3] [1;2;3];;
val it : int list = [1; 2; 3; 2; 4; 6; 3; 6; 9]
答案 2 :(得分:3)
或者我们可以实现一般的交叉产品功能:
let cross l1 l2 =
seq { for el1 in l1 do
for el2 in l2 do
yield el1, el2 };;
并使用此功能完成工作:
cross [1..10] [1..10] |> Seq.map (fun (a,b) -> a*b) |> Seq.toList
答案 3 :(得分:2)
要在没有for
循环的情况下实现相同的功能,您可以使用Mau发布的高阶函数来使用解决方案,或者您可以使用递归显式编写相同的东西:
let cross xs ys =
let rec crossAux ol2 l1 l2 =
match l1, l2 with
// All elements from the second list were processed
| x::xs, [] -> crossAux ol2 xs ol2
// Report first elements and continue looping after
// removing first element from the second list
| x::xs, y::ys -> (x, y)::(crossAux ol2 l1 ys)
// First list is empty - we're done
| [], _ -> []
crossAux ys xs ys
如果您正在学习函数式编程和递归,这可能很有用,但是,使用序列表达式的解决方案实际上更有用。
作为附注,Mau的第一个版本可以更好一点,因为您可以通过调用List.map
来加入对List.collect id
的调用(您可以通过嵌套将lambda直接处理为collect
的参数。 cross
函数看起来像这样(当然,您可以修改此参数以应用于两个数字而不是创建元组):
let cross xs ys =
xs |> List.collect (fun v1 ->
ys |> List.map (fun v2 -> (v1, v2)))
顺便提一下,我的书中有一个free chapter,它讨论了序列表达式和List.collect
函数的工作原理。值得注意的是,序列表达式中的for
直接对应于List.collect
,因此您只需使用此高阶函数即可编写代码:
let cross xs ys =
xs |> List.collect (fun v1 ->
ys |> List.collect (fun v2 -> [(v1, v2)] ))
但是,请参阅免费章节以获取更多信息: - )。