在用F#编写通用代码时,我经常遇到类似以下情况(我知道这样做效率很低,仅出于演示目的):

let isPrime n =
    let sq = n |> float |> sqrt |> int
    {2..sq} |> Seq.forall (fun d -> n % d <> 0)

对于许多问题,我可以使用statically resolved types并由于内联而获得性能提升。
let inline isPrime (n:^a) =
    let two = LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne
    let sq = n |> float |> sqrt |> int
    {two..sq} |> Seq.forall (fun d -> n % d <> LanguagePrimitives.GenericZero)

由于序列上限为浮点数,因此上面的代码无法编译。通常,我可以回溯到int例如。

但是编译器不允许我使用以下任何一种:
  • let sq = n |> float |> sqrt :> ^a
  • let sq = n |> float |> sqrt :?> ^a

  • 这两个导致InvalidCastException:
  • let sq = n |> float |> sqrt |> box |> :?> ^a
  • let sq = n |> float |> sqrt |> box |> unbox

  • 另外,禁止使用upcastdowncast
    let sq = System.Convert.ChangeType(n |> float |> sqrt, n.GetType()) :?> ^a有效,但对我来说却很麻烦。

    有没有一种我可以忽略的方式,或者我真的必须使用最新版本吗?因为最后一个也会破坏bigint,我经常需要它。

    最佳答案

    利用FsControl的技巧,我们可以定义通用函数fromFloat:

    open FsControl.Core
    
    type FromFloat = FromFloat with
        static member instance (FromFloat, _:int32 ) = fun (x:float) -> int x
        static member instance (FromFloat, _:int64 ) = fun (x:float) -> int64 x
        static member instance (FromFloat, _:bigint ) = fun (x:float) -> bigint x
    let inline fromFloat (x:float):^a = Inline.instance FromFloat x
    
    let inline isPrime (n:^a) =
        let two = LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne
        let sq = n |> float |> sqrt |> fromFloat
        {two..sq} |> Seq.forall (fun d -> n % d <> LanguagePrimitives.GenericZero)
    
    printfn "%A" <| isPrime 71
    printfn "%A" <| isPrime 6L
    printfn "%A" <| isPrime 23I
    

    Inline.instance定义为here

    关于casting - 如何将值转换为以前的值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25548710/

    10-09 05:45
    查看更多