F# 中的通用字符串到整数转换
Generic string to integer conversion in F#
我正在 F# 中实现一个简单的字符串到整数转换器;逻辑是,例如从 string
到 uint32
:
let inline sToUint s =
let mutable v = 0u
for c in s do
v <- v * 10u + uint32 c - uint32 '0'
v
现在我想将此实现用于其他整数类型,例如uint64
。我怎样才能实现一个通用函数来做到这一点?
谢谢大家,SRTP(如答案中所用)是我在这种情况下所需要的。以下实现与@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
你可以这样做:
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
测试代码:
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
请注意,sToNum
returns 是一个 lambda,因此不必每次都重新计算 ten
。 (您甚至可以缓存 f '0'
的值,以节省更多时间。)
如果出于实际原因,最好使用 SRTP 创建可用于解析任何具有静态方法的函数 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
请注意,如果输入的格式无效,parse
将抛出异常。为了能够处理这个问题,您可以使用 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
请注意,这两个函数都将使用 CultureInfo.CurrentCulture
作为默认值。它们可以编辑使用CultureInfo.InvariantCulture
,但我会把它留给作业
我正在 F# 中实现一个简单的字符串到整数转换器;逻辑是,例如从 string
到 uint32
:
let inline sToUint s =
let mutable v = 0u
for c in s do
v <- v * 10u + uint32 c - uint32 '0'
v
现在我想将此实现用于其他整数类型,例如uint64
。我怎样才能实现一个通用函数来做到这一点?
谢谢大家,SRTP(如答案中所用)是我在这种情况下所需要的。以下实现与@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
你可以这样做:
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
测试代码:
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
请注意,sToNum
returns 是一个 lambda,因此不必每次都重新计算 ten
。 (您甚至可以缓存 f '0'
的值,以节省更多时间。)
如果出于实际原因,最好使用 SRTP 创建可用于解析任何具有静态方法的函数 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
请注意,如果输入的格式无效,parse
将抛出异常。为了能够处理这个问题,您可以使用 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
请注意,这两个函数都将使用 CultureInfo.CurrentCulture
作为默认值。它们可以编辑使用CultureInfo.InvariantCulture
,但我会把它留给作业