【问题标题】:F# -- The resulting type would be infinite errorF# -- 结果类型将是无限错误
【发布时间】:2016-11-02 14:05:09
【问题描述】:

这是一个生成所有排列的简单代码,基于此处找到的实现:All permutations of a list

let concatElement element sequence =
    seq {
        yield element
        yield! sequence
    }

let rec permute (choices : 'a seq)=
    seq {
        if Seq.isEmpty choices then
            yield Seq.empty
        else
            for choice in choices do
                let remaining = choices |> Seq.where (fun el -> el <> choice)
                yield concatElement choice (permute remaining)
    }

我在"yield concatElement choice (permute remaining)" 上收到一个编译时错误“当统一 ''a' 和 'seq' 时,结果类型将是无限的”

这里有什么问题?

【问题讨论】:

  • 返回类型应该是什么?假设它应该是seq&lt;seq&lt;'a&gt;&gt;,您需要最后一行concatElement 的第一个参数是seq&lt;'a&gt;,但choice'a
  • concatElement 只是将一个元素附加到一个序列并返回 seq,因此 yield concatElement 应该产生一个 seq,结果会产生 seq>。我错过了什么?或者也许我应该传递一个“头”并连接两个序列?
  • 如果permute 返回一个seq&lt;seq&lt;'a&gt;&gt;,那么concatElement choice (permute remaining) 试图连接一个'a 和一个seq&lt;seq&lt;'a&gt;&gt;,这是一个类型错误。如果您向 permute 添加显式返回类型,您可能会收到更有用的错误消息。
  • 我引用了另一个实现,但完全忽略了嵌套循环。为我感到羞耻。我更愿意将其更改为尾递归,但这似乎并不简单

标签: f#


【解决方案1】:

您正在尝试将yield'a seq 作为具有'a seq 类型的序列的单个元素。为此,'a 必须与 'a seq 的类型相同,依此类推,因此类型为“无限” - 您必须无限嵌套它们才能“工作”。

我在您的回答中看到您认识到您必须以某种方式循环遍历序列元素。您可以使用 for .. in 语法进行迭代,或者 F# 为您提供 yield! 运算符,它实际上为您执行此操作。本着这种精神,你的答案可以写成

let rec permute (choices : 'a seq) (permBuilder: 'a seq) : seq<seq<'a>>= seq {
    if Seq.isEmpty choices then
        yield permBuilder
    else
        for choice in choices do
            let remaining = choices |> Seq.where (fun el -> el <> choice)
            let newBuilder = concatElement choice permBuilder 
            yield! permute remaining newBuilder }

或者,或者,我们可以使用Seq.collect 函数为我们自动收集序列序列(序列!),我们留下了没有显式迭代器的漂亮的函数式代码:

let prepend element sequence = seq { yield element; yield! sequence }

let rec permute input =
    if Seq.length input <= 1 then Seq.singleton input
    else
        let without element = Seq.where ((<>) element) input
        let permutations element = permute (without element) |> Seq.map (prepend element)
        Seq.collect permutations input

但需要考虑的一点是:如果您有两个不同的元素会发生什么?例如,如果您尝试获取[1; 1; 2] 的排列会发生什么?您当前的方法无法巧妙地处理这个问题 - 即使您改进了 without 闭包,您也会得到重复的答案。

【讨论】:

    【解决方案2】:

    我想我明白了

    let concatElement element sequence =
        seq {
            yield element
            yield! sequence
        }
    
    let rec permute (choices : 'a seq) (permBuilder: 'a seq) : seq<seq<'a>>=
        seq {
            if Seq.isEmpty choices then
                yield permBuilder
            else
                for choice in choices do
                    let remaining = choices |> Seq.where (fun el -> el <> choice)
                    let newBuilder = concatElement choice permBuilder 
                    for perm in permute remaining newBuilder do
                        yield perm
    

    正如 Lee 所说,我尝试将 seq 与 seq> 连接起来。我的代码缺少对排列的循环。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-05
      • 1970-01-01
      • 2019-11-16
      • 2021-03-08
      相关资源
      最近更新 更多