简体   繁体   中英

Generic string to integer conversion in F#

I'm implementing a simple conversion string to integer convertor in F#; the logic is, eg from string to uint32 :

let inline sToUint s =
    let mutable v = 0u

    for c in s do
        v <- v * 10u + uint32 c - uint32 '0'

    v

Now I would like to use this implementation for other integer types, eg uint64 . How can I implement a single generic function to do that?

You can do something like this:

let inline sToNum f =
    let ten = Seq.replicate 10 LanguagePrimitives.GenericOne |> Seq.sum
    fun s ->
        let mutable v = LanguagePrimitives.GenericZero
        for c in s do
            v <- v * ten + f c - f '0'
        v

let sToUint16 = sToNum uint16
let sToUint32 = sToNum uint32
let sToUint64 = sToNum uint64
let sToInt16 = sToNum int16
let sToInt32 = sToNum int32
let sToInt64 = sToNum int64

Test code:

sToUint16 "123" |> printfn "%A"   // 123us
sToUint32 "123" |> printfn "%A"   // 123u
sToUint64 "123" |> printfn "%A"   // 123UL
sToInt16 "123" |> printfn "%A"    // 123s
sToInt32 "123" |> printfn "%A"    // 123
sToInt64 "123" |> printfn "%A"    // 123L

Note that sToNum returns a lambda, so it doesn't have to recompute ten each time. (You could even cache the value of f '0' as well, to shave off a little more time.)

If it's done for practical reasons, it's better to use SRTP to create function that could be used to parse anything that have static method Parse: string -> ^a

let inline parse (input: string) : ^a =
    ( ^a: (static member Parse: string -> ^a) (input))

(parse "123" : int) |> printfn "%A"
(parse "123" : int64) |> printfn "%A"
(parse "123" : byte) |> printfn "%A"
(parse "123" : int16) |> printfn "%A"
(parse "123" : float32) |> printfn "%A"
(parse "123" : double) |> printfn "%A"
(parse "123" : string) |> printfn "%A" // fails to compile, as string doesn't have Parse method

Note that parse throws exception if input in invalid format. To be able to handle that, you can use TryParse active pattern

let inline (|TryParse|_|) (input: string) : ^a option =
    let mutable result = Unchecked.defaultof< ^a >
    let success = ( ^a: (static member TryParse: string * ^a byref -> bool) (input, &result))
    if success then
        Some result
    else
        None

match "123" with
| TryParse (a : int) -> printfn "it's int %d" a
| TryParse (a : int64) -> printfn "it's very big int %d" a
| TryParse (a : float) -> printfn "it's double %f" a
| x -> printfn "it's something I can't handle: %s" x

Note that both functions will use CultureInfo.CurrentCulture as default. They can be edited to use CultureInfo.InvariantCulture , but I will leave it for homework

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM