通用类型的F#问题

Bea*_*ker 2 generics f# types annotations c#-to-f#

我正在尝试将一些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
Run Code Online (Sandbox Code Playgroud)

一切都很顺利,直到我开始使用Gene not taken方法.这是该方法的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);
    }
Run Code Online (Sandbox Code Playgroud)

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

"通用成员'GeneNotTaken'已在此程序点之前的非统一实例化中使用.考虑重新排序成员,使该成员首先出现.或者,明确指定成员的完整类型,包括参数类型,返回类型和任何其他通用参数和约束."

"这段代码的通用性低于其注释所要求的,因为显式类型变量'GeneType'不能一概而论.它被限制为'单位'."

你会认为第一个错误是清楚的,除非你可以看到我之前没有使用GeneNotTaken成员,这就是为什么我不知道问题是什么.

我的问题的第二部分是如何在方法的末尾添加返回默认值('GeneType).

如果您有一些其他建议来改进我的代码,请随时分享.

Tom*_*cek 7

出现错误消息的原因是您的实现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
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

为了给出一些一般的提示,我可能不会使用Unchecked.defaultof<'GeneType>相反,option当你处理可能缺少值的情况时,你应该使用类型.GeneNotTaken那么结果类型就是option<'GeneType>.而不是match你可以写:

  trial |> Option.map (fun actualTrial ->
      taken.Add(actualTrial)
      actualTrial )
Run Code Online (Sandbox Code Playgroud)

此外,您的代码使用了大量的突变,这在F#中编写功能代码时可能不是最好的做法.但是,如果您只是学习F#,那么最好将一些C#代码重写为F#.随着你了解更多,你应该寻找避免变异的方法,因为它会使你的F#代码更加惯用(编写它也会更有趣!)

  • 我想你会注意到这个错字,但最后的循环0在我认为答案的第一部分是越位 (2认同)