代码之家  ›  专栏  ›  技术社区  ›  Alex Humphrey

如何计算F#中n个序列的笛卡尔积?

  •  12
  • Alex Humphrey  · 技术社区  · 14 年前

    我收到一个拼图作为礼物。它由4个立方体组成,并排排列。每个立方体的面是四种颜色中的一种。

    为了解决这个难题,立方体必须确定方向,使四个立方体的顶部不同,前部不同,背部不同,底部不同。左右两边不重要。

    1. 创建每个
    2. 获取所有可能的方向 每个立方体(每个立方体有24个)。
    3. 获取所有可能的组合 每个立方体的方向。
    4. 满足解决方案。

    我用F#中伪代码的实现解决了这个难题,但对我执行步骤3的方式并不满意:

    let problemSpace =
        seq { for c1 in cube1Orientations do
                  for c2 in cube2Orientations do
                      for c3 in cube3Orientations do
                          for c4 in cube4Orientations do
                              yield [c1; c2; c3; c4] }
    

    上面的代码非常具体,只计算出四个方向序列的笛卡尔积。我开始考虑一种方法来写n个方向的序列。

    我想到了(从现在开始,所有的代码都应该在F#interactive中很好地执行):

    // Used to just print the contents of a list.
    let print = 
        Seq.fold (fun s i -> s + i.ToString()) "" >> printfn "%s"
    
    // Computes the product of two sequences - kind of; the 2nd argument is weird.
    let product (seq1:'a seq) (seq2:'a seq seq) =
        seq { for item1 in seq1 do
                  for item2 in seq2 do
                      yield item1 |> Seq.singleton |> Seq.append item2 }
    

    seq { yield Seq.empty }
    |> product [ 'a'; 'b'; 'c' ]
    |> product [ 'd'; 'e'; 'f' ]
    |> product [ 'h'; 'i'; 'j' ]
    |> Seq.iter print
    

    ... 导致。。。

    let productn (s:seq<#seq<'a>>) =
        s |> Seq.fold (fun r s -> r |> product s) (seq { yield Seq.empty })
    
    [ [ 'a'; 'b'; 'c' ]
      [ 'd'; 'e'; 'f' ]
      [ 'h'; 'i'; 'j' ] ]
    |> productn
    |> Seq.iter print
    

    然而,使用product涉及到讨厌的seq{yield seq.empty}行,它不直观地需要:

    1. 一系列值(seq<'a>)
    2. 序列 序列的

    productn很好地隐藏了这个奇怪的接口,但不管怎样,它仍然困扰着我。

    有没有更好,更直观的方法来计算n序列的笛卡尔积?是否有任何内置函数(或其组合)可以做到这一点?

    3 回复  |  直到 14 年前
        1
  •  12
  •   cfern    14 年前

    使用递归:n个列表的笛卡尔积{L1..LN}是将L1中的每个元素添加到列表{L2..LN}的笛卡尔积的每个子列表时得到的列表集合。

    let rec cart1 LL = 
        match LL with
        | [] -> Seq.singleton []
        | L::Ls -> seq {for x in L do for xs in cart1 Ls -> x::xs}
    

    例子:

    > cart1 [[1;2];[3;4;5];[6;7]] |> Seq.toList;;
    val it : int list list =
      [[1; 3; 6]; [1; 3; 7]; [1; 4; 6]; [1; 4; 7]; [1; 5; 6]; [1; 5; 7]; [2; 3; 6];
       [2; 3; 7]; [2; 4; 6]; [2; 4; 7]; [2; 5; 6]; [2; 5; 7]]
    

        2
  •  1
  •   Colonel Panic JaredPar    6 年前

    这里有一个解决方案 'a list list -> Seq<'a list> 计算n个列表的笛卡尔积,使用惰性求值。我写它是为了模仿Python itertools.product

    let product lists = 
        let folder list state =
             state |> Seq.allPairs list |> Seq.map List.Cons 
        Seq.singleton List.empty |> List.foldBack folder lists
    

    它是基于 List.allPairs 它是在F#4.0中引入的。

        3
  •  0
  •   TechNeilogy    14 年前

    下面是列表版本的第一次尝试。我想可以清理一下。

    let rec cart nll = 
      let f0 n nll =
        match nll with
        | [] -> [[n]]
        | _ -> List.map (fun nl->n::nl) nll
      match nll with
      | [] -> []
      | h::t -> List.collect (fun n->f0 n (cart t)) h