两个列表的交叉产品

时间:2009-01-27 10:16:48

标签: .net f#

使用List模块的“扩展函数”进行处理。 (我花了很长时间开发'mapfold' - 它将一个累加器像fold折叠,但是用它作为参数来创建像map这样的新值 - 然后发现那就是List.scan_left做的那样)

为了生成测试数据,我需要做两个列表的交叉产品,这就是我提出的:

///Perform cross product of two lists, return tuple
let crossproduct l1 l2 =
    let product lst v2 = List.map (fun v1 -> (v1, v2)) lst
    List.map_concat (product l1) l2

这有什么用,还是已经有更好的方法呢?

同样的问题:

///Perform cross product of three lists, return tuple
let crossproduct3 l1 l2 l3 =
    let tuplelist = crossproduct l1 l2 //not sure this is the best way...
    let product3 lst2 v3 = List.map (fun (v1, v2) -> (v1, v2, v3)) lst2
    List.map_concat (product3 tuplelist) l3

5 个答案:

答案 0 :(得分:24)

另一个选择是使用F#“序列表达式”并写下这样的东西:

let crossproduct l1 l2 =
  seq { for el1 in l1 do
          for el2 in l2 do
            yield el1, el2 };;

(实际上,它与您所写的内容几乎相同,因为'for .. in .. do'在序列表达式中可以被视为map_concat)。这适用于(懒惰)序列,但如果你想使用列表,你只需将代码包装在[...]内而不是seq {...}内。

答案 1 :(得分:4)

刚刚使用计算表达式遇到了一个相当优雅的解决方案:

type Product () =
  member this.Bind (l,f) = List.collect f l    
  member this.Return n = [n]

let enumeratedPizzas = 
  Product() {
    let! x = ["New York";"Chicago"]
    let! y = ["Pepperoni";"Sausage"]
    let! z = ["Cheese";"Double Cheese"]
    return x,y,z
  }

Techneilogy复制fssnip.net,点击链接查看评论代码。

答案 2 :(得分:1)

您的crossproduct功能看起来不错(您可能已经注意到缺少“in”关键字)。我更喜欢这个版本的crossproduct3,但那只是我:

let crossproduct3 l1 l2 l3 = 
List.map_concat 
(fun z ->
 (List.map_concat (fun y -> List.map (fun x -> (x, y, z)) l3) l2)) l1;;

您的函数具有相同的算法复杂度。

最后,当在明确的列表中使用crossproduct时,您可能会点击 value restriction (粗略地说,一个限制,确保编译器只能推断语法值的多态类型),在F#中特别严格。解决方案是按以下方式注释使用空列表的调用(如果您希望第二个列表由整数组成):

(crossproduct [3; 4] [] : (int * int) list)

答案 3 :(得分:1)

我使用Benjol回答了一段时间,直到我发现你可以用Linq做同样的事情。 Benjol的解决方案可能仍然是最优雅的,但是如果有人想要一个可以与C#一起使用的解决方案,那么你就去了:

query {
  for i in [1, 2] do
  for j in [3, 4] do
  select (i, j)
}

这与Tomas Petricek的解决方案几乎完全相同,除了解决方案不需要嵌套for循环,因此它稍微简单。

答案 4 :(得分:0)

我最近需要类似的东西 - 我必须将序列列表压缩到一系列列表 - 所以[(a1,a2,a3,...);(b1,b2,b3,...);(c1,c2,c3,...)] -> ([a1;b1;c1], [a2;b2;c3], ...)

以下代码将执行此操作:

   let rec listZip (sl : 'a seq list) =   
       seq {  
           match sl with  
           | [] -> yield []  
           | hd::tl ->  
              for h in hd do  
              for t in listZip tl do  
              yield (h::t)  
   }