在用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
另外,禁止使用
upcast
和downcast
。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/