【问题标题】:F#: how to find Cartesian powerF#:如何找到笛卡尔幂
【发布时间】:2011-02-08 13:32:01
【问题描述】:

我在编写笛卡尔幂函数时遇到问题。我找到了很多关于计算笛卡尔积的例子,但没有一个关于笛卡尔幂的例子。
例如,[1;2] 的幂 3 = [ [1;1;1] ; [1;1;2] ; [1;2;1] ; [1;2;2] ; [2;1;1] ; [2;1;2] ; [2;2;1]; [2;2;2] ]
我使用以下代码来计算笛卡尔积:

 let Cprod U V =
        let mutable res = []
        for u in U do
            for v in V do
                res <- res @ [[u;v]]
        res

并尝试计算笛卡尔幂。 我使用以下代码来计算笛卡尔积:

let Cpower U n =
    let mutable V = U
    for i=0 to n-1 do
        V <- Dprod U V
    V

Visual Studio 说:错误统一''a'和''a list'时产生的类型将是无限的。我会感谢任何帮助和链接。

【问题讨论】:

  • 你知道 @ 有线性运行时间吗?使用 :: 并在末尾反转(如果有必要的话)将比使用 @ 快得多。

标签: f# functional-programming


【解决方案1】:

至于错误来自哪里,我们有以下类型约束

// Cprod: seq<`a> -> seq<`a> -> `a list list
let Cprod U V =
    ...

// Cpower: seq<`a> -> int -> ???
let Cpower U n =
    // V: seq<`a>
    let mutable V = U
    // n: int
    for i=0 to n-1 do
        (* The next line implies two type constraints:
           V: seq<`a>
           V: `a list list *)
        V <- Dprod U V
    V

V 必须是 seq&lt;`a&gt; 和 `a list list,并且 U 和 V 必须具有相同的类型意味着 `a = `a list,这就是导致“无限类型”错误消息(infinity类型是... list list list list。即使V 的值是可变的,它也必须只有一个类型。

【讨论】:

    【解决方案2】:

    我还要补充一点,在编写 F# 代码时,通常最好避免使用 mutable 值。在学习 F# 或者需要优化某些代码以更快运行时,这很好,但如果您想编写更惯用的 F# 代码,最好使用递归而不是 mutable 值。

    我试图更优雅地写笛卡尔幂,这是我的版本。它是递归实现的。当我们需要计算 X^1 并且递归情况执行笛卡尔积时,我明确处理这种情况:X^n = X * X^(n-1)

    我正在使用序列表达式,该方法使用yield 生成序列元素(作为结果返回):

    let rec cartesianPow input n = seq {
      if (n = 1) then
        // This handles the case when the recursion terminates. We need to turn
        // each element from the input into a list containing single element:
        //   [1; 2; 4] ^ 1 = [ [1]; [2]; [3] ]
        for el in input do 
          yield [el]
      else
        // We perform one Cartesian product (and run the rest of the 
        // power calculation recursively). Mathematically:
        //   [1; 2; 3] ^ n = [1; 2; 3] x ([1; 2; 3] ^ (n-1))
        for el in input do 
          for rest in cartesianPow input (n - 1) do
            yield el :: rest }
    
    cartesianPow [ 0; 1 ] 3
    

    这不是最有效的实现方式(例如,因为在 for 循环中使用 yield 可能不是一件好事),但这对于大型 n 来说是唯一的问题。在 F# 中,从最简洁、更易于理解的实现开始通常是一个好主意:-)。

    【讨论】:

      【解决方案3】:

      这是从 Haskell 移植的版本:

      let replicate n x = [for i in 1 .. n -> x]
      let sequence ms = 
        List.fold (fun m' m -> [for x in m do for xs in m' -> (x::xs)]) [[]] ms
      let Cpower n l = replicate n l |> sequence
      

      它的工作原理类似于计数:如果您将 l 视为数字,那么它会根据您拥有的位置数复制它们,然后使用 sequence 对它们进行计数。

      换句话说,所有小于 2^3 的二进制数都可以通过将[0;1] 复制 3 次得到[[0;1]; [0;1]; [0;1]] 然后对其运行sequence 来生成。

      这可以通过切换到Seq.fold来变得更懒惰:

      let sequence' ms =
        Seq.fold (fun m' m -> seq {for x in m do for xs in m' do yield (x::xs)})
                 (seq {yield []})
                 ms
      

      这将为您提供结果的序列而不是列表。不幸的是,我无法通过查看它来判断它是否懒惰足够:它可能必须在内存中生成整个列表才能开始为您提供第一个元素。您应该能够通过在调试器中单步执行来找出答案。 (或者你可能比我更擅长阅读懒惰。)

      【讨论】:

        【解决方案4】:

        我解决了我的问题:

        let rec Cprod = function
            | [] -> [[]]
            | hs::tss ->
                [ for h in hs do            
                    for ts in D tss ->
                        h::ts]
        
        let Cpower U n = 
            let mutable inp = []
            for i=0 to n-1 do
                inp <- inp @ [U]
            Dprod inp
        

        【讨论】:

          猜你喜欢
          • 2018-07-06
          • 2019-03-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-15
          • 2012-09-07
          • 2012-05-07
          • 1970-01-01
          相关资源
          最近更新 更多