【问题标题】:F# Problems With Generic Types泛型类型的 F# 问题
【发布时间】:2011-07-14 06:06:55
【问题描述】:

我正在尝试将一些 C# 代码转换为 F#,但遇到了一个小问题。这是我已经拥有的 F# 代码:

open System
open System.Collections
open System.Collections.Generic

type Chromosome<'GeneType>() =
    let mutable cost = 0
    let mutable (genes : 'GeneType[]) = Array.zeroCreate<'GeneType> 0
    let mutable (geneticAlgorithm : GeneticAlgorithm<'GeneType>) = new GeneticAlgorithm<'GeneType>()

    /// The genetic algorithm that this chromosome belongs to.
    member this.GA
        with get() = geneticAlgorithm
        and set(value) = geneticAlgorithm <- value

    /// The genes for this chromosome.
    member this.Genes
        with get() = genes
        and set(value) = genes <- value

    /// The cost for this chromosome.
    member this.Cost
        with get() = cost
        and set(value) = cost <- value

    /// Get the size of the gene array.
    member this.Size = genes.Length

    /// Get the specified gene.
    member this.GetGene(gene:int) =
        genes.[gene]

    member this.GeneNotTaken(source:Chromosome<'GeneType>, taken:IList<'GeneType>) =
        let geneLength = source.Size
        for i in 0 .. geneLength do
            let trial = source.GetGene(i)
            if(not (taken.Contains(trial))) then
                taken.Add(trial)
                trial

一切都很顺利,直到我开始使用基因未采取方法。这是该方法的 C# 代码(我还需要帮助返回默认类型,但还没有做到那么远):

private GENE_TYPE GetNotTaken(Chromosome<GENE_TYPE> source,
            IList<GENE_TYPE> taken)
    {
        int geneLength = source.Size;

        for (int i = 0; i < geneLength; i++)
        {
            GENE_TYPE trial = source.GetGene(i);
            if (!taken.Contains(trial))
            {
                taken.Add(trial);
                return trial;
            }
        }

        return default(GENE_TYPE);
    }

我看到的编译器错误包括:

“通用成员 'GeneNotTaken' 已在此程序点之前的非统一实例化中使用。考虑重新排序成员,以便首先出现此成员。或者,显式指定成员的完整类型,包括参数类型,返回类型以及任何其他泛型参数和约束。”

“此代码没有其注释要求的通用性,因为显式类型变量 'GeneType' 无法泛化。它被限制为 'unit'。”

您会认为第一个错误非常清楚,除非您可以看到在那之前我没有使用 GeneNotTaken 成员,这就是为什么我不知道问题出在哪里。

我的问题的第二部分是如何在方法的末尾添加 return default('GeneType) 。

如果您对改进我的代码有其他建议,请随时分享。

【问题讨论】:

    标签: generics f# types annotations c#-to-f#


    【解决方案1】:

    错误消息的原因是您的GeneTaken 实现实际上并未返回trial 值。问题是 F# 没有命令式 return 语句。

    在 F# 中,if .. then .. 被视为计算并给出一些结果的表达式。例如,您可以写let a = if test then 10 else 12。当您省略 else 分支时,语句的主体必须是一些返回 unit(表示无返回值的类型)的命令式操作。你不能写let a = if test then 42 - 如果test = false,结果的值是多少?

    您可以通过使用递归循环编写方法来修复它 - 然后您有一个实际返回 trial 的方法,因此 F# 类型检查器不会混淆:

    member this.GeneNotTaken
        (source:Chromosome<'GeneType>, taken:IList<'GeneType>) : 'GeneType =
      let geneLength = source.Size
      let rec loop i =
        if i >= geneLength then Unchecked.defaultof<'GeneType> // Return default
        let trial = source.GetGene(i)
        if (not (taken.Contains(trial))) then
          // Gene was found, process it & return it
          taken.Add(trial)
          trial
        else 
          // Continue looping
          loop (i + 1)
      loop 0
    

    使用Seq.tryPick 函数的替代(可能更好)实现:

    member this.GeneNotTaken
        (source:Chromosome<'GeneType>, taken:IList<'GeneType>) : 'GeneType =
      let geneLength = source.Size
      // Find gene that matches the given condition
      // returns None if none exists or Some(trial) if it was found
      let trial = [ 0 .. geneLength - 1 ] |> Seq.tryPick (fun i ->
        let trial = source.GetGene(i)
        if (not (taken.Contains(trial))) then Some(trial) else None) 
      match trial with 
      | Some(trial) ->
          // Something was found
          taken.Add(trial)
          trial
      | _ -> 
          Unchecked.defaultof<'GeneType> // Return default
    

    为了给出一些一般性的提示,我可能不会使用Unchecked.defaultof&lt;'GeneType&gt; 相反,当您处理可能缺少值的情况时,您应该使用option 类型。 GeneNotTaken 的结果类型将是 option&lt;'GeneType&gt;。你可以写而不是match

      trial |> Option.map (fun actualTrial ->
          taken.Add(actualTrial)
          actualTrial )
    

    此外,您的代码使用了很多突变,这可能不是在 F# 中编写函数式代码时最好的做法。但是,如果您只是学习 F#,那么最好先将一些 C# 代码重写为 F#。随着您了解更多,您应该寻找避免突变的方法,因为它会使您的 F# 代码更加地道(而且编写它也会更有趣!)

    【讨论】:

    • 我猜你会注意到错字,但我认为答案的第一部分中最后的循环 0 越位了
    猜你喜欢
    • 1970-01-01
    • 2012-12-19
    • 2011-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多