Vraag Hoe kan ik teruggooien naar een type waarvan de waarde eerder was?


Heel vaak kom ik bij het schrijven van generieke code in F # langs een vergelijkbare situatie (ik weet dat dit zo is) vrij inefficiënt, alleen voor demonstratiedoeleinden):

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

Voor veel problemen die ik kan gebruiken statisch opgeloste types en krijg zelfs een prestatieverbetering door 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)

De bovenstaande code compileert niet omdat de bovenste sequentielimiet een float is. Nongenerically, kon ik enkel terug naar werpen int bijvoorbeeld.

Maar de compiler laat me geen van deze gebruiken:

  • let sq = n |> float |> sqrt :> ^a
  • let sq = n |> float |> sqrt :?> ^a

en deze twee leidden tot a InvalidCastException:

  • let sq = n |> float |> sqrt |> box |> :?> ^a
  • let sq = n |> float |> sqrt |> box |> unbox

Ook, upcast en downcast zijn verboden.

let sq = System.Convert.ChangeType(n |> float |> sqrt, n.GetType()) :?> ^a werkt, maar lijkt me erg omslachtig.

Is er een manier die ik over het hoofd heb gezien of moet ik echt de laatste versie gebruiken? Omdat de laatste ook zal breken bigint, wat ik vrij vaak nodig heb.


10
2017-08-28 12:20


oorsprong


antwoorden:


Met de truc van FsControl, we kunnen de generieke functie definiëren 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 is gedefinieerd hier.


4
2017-08-29 11:57