Как я могу вернуть обратно тип, который был ранее?
Очень часто при написании родового кода в F # я сталкиваюсь с ситуацией, подобной этой (я знаю, что это довольно неэффективно, просто для демонстрационных целей):
let isPrime n =
let sq = n |> float |> sqrt |> int
{2..sq} |> Seq.forall (fun d -> n % d <> 0)
Для многих проблем я могу использовать статически разрешенные типы и получить даже повышение производительности из-за inlining.
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
, что мне нужно довольно часто.
Ответы
Ответ 1
С трюком 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 был определен здесь.