我正在尝试将一些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
一切都很顺利,直到我开始使用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);
}
我看到的编译器错误包括:
“通用成员'GeneNotTaken'已在此程序点之前的非统一实例中使用。考虑重新排序成员,以便首先发生此成员。或者,明确指定成员的完整类型,包括参数类型,返回类型和任何其他通用参数和约束。“
和
“此代码的通用性低于其注释所要求的,因为显式类型变量'GeneType'无法一般化。它被限制为'unit'。”
你会认为第一个错误是清楚的,除非你可以看到我之前没有使用GeneNotTaken成员,这就是为什么我不知道问题是什么。
我的问题的第二部分是如何在方法的末尾添加返回默认值('GeneType)。
如果您有一些其他建议可以改进我的代码,请随时分享。
答案 0 :(得分: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#代码更加惯用(编写它也会更有趣!)