【问题标题】:Problems with generics and casting in F#F# 中的泛型和强制转换问题
【发布时间】:2014-01-14 21:09:51
【问题描述】:

使用以下列方式实现的基因组类型

type Genome<'T> (listIn : List<'T>)=
    member this.list = listIn
    member this.GetValues() =
        this.list

我在编写另一个代码片段时遇到了问题。

type IMutation =
    abstract member MutateMethod: Genome<'T> -> Genome<'T>

type DoubleGenomeMutation() = 
    member this.Mutate = (this :> IMutation).MutateMethod
    interface IMutation with
        member this.MutateMethod(genome: Genome<'T>) = 
            let randomIndex = MyRandom.Next(0, genome.list.Length)
            printfn "%d" randomIndex
            let newValue = MyRandom.NextDouble(Parameters.LowerBounds.Item(randomIndex), Parameters.UpperBounds.Item(randomIndex))

            let (newGenomeList : List<'double>, _) = 
                List.foldBack (fun elem acc -> 
                match acc with
                | (l, 0) -> (newValue::l, -1)
                | (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

            new Genome<'T>(newGenomeList)

我发现这里不可能正确使用泛型机制。

中引用的示例效果
Error   2   Type mismatch. Expecting a
    float list    
but given a
    List<'a>    
The type 'float' does not match the type ''a'

排队

| (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

在类似情况下,向上或向下转换变量通常被描述为解决方案 - 不幸的是,我无法以这种方式修复我的程序。我将不胜感激任何直接帮助以及有关所有问题根源的任何建议。

【问题讨论】:

  • 我打赌你在顶部有一个 open System.Collections.Generic 并且正在与那里的列表类型造成一些混淆
  • 可能根本没有。我没有在任何地方使用 System.Collections.Generic,只有不可变的 F# 列表。
  • 如果将签名从 List&lt;'a&gt; 更改为 'a list 会发生什么?
  • 不幸的是没有什么令人兴奋的。 Error 2 Type mismatch. Expecting a float list but given a 'a list The type 'float' does not match the type ''a'
  • MyRandom.NextDouble的返回类型是什么?

标签: .net generics casting f# type-conversion


【解决方案1】:

我没有MyRandom 的来源,但如果我将MyRandom.Next 替换为int 看起来应该是这样,并将MyRandom.Double 替换为double,那么我可以重现您的错误。

问题在于MyRandom.NextDouble 的返回类型不适当地约束了其余代码; MutateMethod 被声明为能够接受 any 类型参数,但随后您在 (l, 0) 案例中使用 newValue 作为返回列表的一部分,这也限制了其他案例的类型.

您可能还收到来自newValue 的警告,指出它将您的'double 类型变量限制为float 类型,这是另一个线索。真的'double 应该是'T,因为这就是最初声明参数的方式。

如果我用抛出异常(可以有任何结果类型)替换对MyRandom.NextDouble 的调用,错误就会消失,这证实了这个诊断:

type DoubleGenomeMutation() = 
    member this.Mutate = (this :> IMutation).MutateMethod
    interface IMutation with
        member this.MutateMethod(genome: Genome<'T>) = 
            let randomIndex = 1 // MyRandom.Next(0, genome.list.Length)
            printfn "%d" randomIndex
            let newValue = failwith "foo"

            let (newGenomeList : List<'T>, _) = 
                List.foldBack (fun elem acc -> 
                match acc with
                | (l, 0) -> (newValue::l, -1)
                | (l, i) -> (elem::l, i-1)) (genome.GetValues()) ([], genome.GetValues().Length - randomIndex - 1)

            new Genome<'T>(newGenomeList)

【讨论】:

  • 非常感谢。似乎如果我按照我的方式指定IMutation 接口,任何实现都必须能够处理任何'T 类型。但是是否有任何解决方案可以允许使用多个专门的实现(例如:一个只使用双精度数,一个使用整数等 - 内部有不同的逻辑)?
  • 最干净的方法是定义IMutation 接口的多个MutateMethod 成员 - 我认为您可以共享名称,因为重载决议应该能够解决这个问题。您也可以使用执行类型测试的大匹配语句来实现 MutateMethod,但它需要大量装箱和拆箱来安抚类型检查器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 2010-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多