Reputation: 661
I'm implementing a simple conversion string to integer convertor in F#; the logic is, e.g 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, e.g. uint64
. How can I implement a single generic function to do that?
Thank for all, SRTP (as used in the answers) is what I need for such a situation. The following implementation is just slightly changed from the answer of @Brian Berns
// (char -> ^a) -> (string -> ^b)
let inline sToNum f =
let tenTimes v = (v <<< 3) + (v <<< 1)
fun (s: string) ->
let mutable v = Unchecked.defaultof<_>
for c in s do
v <- (tenTimes v) + f c - f '0'
v
Upvotes: 3
Views: 121
Reputation: 4498
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
Upvotes: 2
Reputation: 17038
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.)
Upvotes: 5