3

我最近一直在玩 F# 。我想知道如何使用 Seq map 函数或类似的东西来生成类似下面的东西,而不是使用 for 循环来生成与列表中所有其他元素相乘的元素序列。

因此,例如,我有一个列表 [1..10] 我想应用一个乐趣,它会产生类似的结果

[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]

我怎样才能做到这一点?

非常感谢您的帮助。

4

4 回答 4

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))
于 2010-06-05T13:37:15.610 回答
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# 中不需要流利的语法,因为我通常将函数与管道 (|>) 运算符链接在一起。

我的方法是使用交叉函数而不是类型本身来扩展 List 模块:

module List =
    let cross f L1 L2 = 
        [for x in L1 do
            for y in L2 -> f x y]

如果这样做,您可以像使用任何其他 List 方法一样使用 cross 方法:

> 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]
于 2010-06-05T13:38:26.543 回答
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
于 2010-06-05T16:09:27.563 回答
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))) 

顺便说一句,我的书中有一章免费提供List.collect,其中讨论了序列表达式和函数的工作原理。值得注意的是,for序列表达式直接对应于List.collect,因此只需使用这个高阶函数即可编写代码:

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.collect (fun v2 -> [(v1, v2)] )) 

但是,请参阅免费章节了解更多信息 :-)。

于 2010-06-05T22:06:39.527 回答