2011-03-12 90 views
2

我試圖將一些C#代碼轉換爲F#並遇到輕微問題。這裏是我已經的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'已經在該程序點之前的非統一實例化中使用,考慮重新排序成員,以便該成員首先出現,或者明確指定成員的完整類型,包括參數類型,返回類型和任何其他通用參數和約束。「

「本代碼是比所需由其註釋,因爲明確的類型變量‘基因型’不能一概而論較少通用的。它被約束爲‘單元’。」

您會認爲第一個錯誤會很明顯,除非您可以看到我沒有在那之前使用GeneNotTaken成員,這就是爲什麼我不知道問題是什麼。

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

如果您有其他一些改進我的代碼的建議,請隨時與我們分享。

回答

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 

另一種(也許更好)實現使用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#代碼更具慣用性(編寫它也會更有趣!)

+0

再次感謝Tomas! – Beaker 2011-03-13 00:23:13

+2

我想你會注意到這個錯字,但是最後一個循環0在答案的第一部分越位我認爲 – FinnNk 2011-03-13 00:25:12

+0

@FinnNk:謝謝你指出。我確定了答案。 – 2011-03-13 01:37:07