簡體   English   中英

泛型類型的F#問題

[英]F# Problems With Generic Types

我正在嘗試將一些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)。

如果您總體上對我的代碼有其他建議,請隨時分享。

出現錯誤信息的原因是您的GeneTaken實現實際上未返回trial值。 問題在於F#沒有命令式return語句。

在F#中, if .. then ..視為評估並給出某些結果的表達式。 例如,您可以編寫let a = if test then 10 else 12 省略else分支時,語句的主體必須是一些命令unit ,該unit必須返回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<'GeneType>而在處理可能缺少值的情況時,應該使用option類型。 然后, GeneNotTaken的結果類型將為option<'GeneType> 除了match您可以這樣寫:

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

另外,您的代碼使用了大量的變異,這在用F#編寫功能代碼時可能不是最好的選擇。 但是,如果您只是在學習F#,那么最好將一些C#代碼重寫為F#開始。 隨着學習的深入,您應該尋找避免突變的方法,因為這會使您的F#代碼更加慣用(並且編寫它也會更加有趣!)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM