我怎样才能将一个值转换回类型?

pri*_*tor 10 f# static-analysis casting compile-time

通常在F#中编写通用代码时,我会遇到与此类似的情况(我知道这非常低效,仅用于演示目的):

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

对于许多问题,我可以使用静态解析的类型,并通过内联获得性能提升.

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)
Run Code Online (Sandbox Code Playgroud)

由于上限序列是一个浮点数,上面的代码将无法编译.非理性地说,我可以退回到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,我经常需要.

小智 4

利用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
Run Code Online (Sandbox Code Playgroud)

Inline.instance在这里定义。