输入检查是否为有效的 double 或 int

Input checking for a valid double or int

我正在尝试创建一个方法,该方法接受一个字符串并尝试根据第二个参数将其解析为 int 或 double。我的代码看起来正确,但给我一个类型不匹配。然而看起来它们是正确的类型...

我希望得到有关如何修复它的建议。下面是我到目前为止的位置。

let double = Type.GetType("System.Double")
let int32 = Type.GetType("System.Int32")

let rec checkinput input (msType:Type)= 
    //force them to give you a good number
    let ConsoleTrap parsed msType=
        if fst parsed then
            snd parsed //return the parsed value
        else //the parse failed
            Console.WriteLine "Enter a real number"
            let newInput = Console.ReadLine()
            checkinput newInput
    //do it slightly differently based on whether we want an int or double
    if msType = double then
        ConsoleTrap (Double.TryParse input) (double)

    else 
        ConsoleTrap (Int32.TryParse input) (int32)

问题的第一部分是 ConsoleTrap 中的递归调用缺少参数。你只给它newInput,但它也期望msType。您可以将该行更改为:

checkinput newInput msType

现在,这仍然行不通,但我们更接近了。现在的问题是不清楚函数的结果应该是什么。在:

if msType = double then
    ConsoleTrap (Double.TryParse input) (double)
else 
    ConsoleTrap (Int32.TryParse input) (int32)

... true 分支 returns floatfalse 分支 returns int。所以这是一个类型检查错误。您可以通过使函数 checkinput 通用并仅处理 intdouble 情况(但使其成为 return 'T)来解决此问题。然后你可以添加不安全的unbox。以下将起作用:

let rec checkinput input : 'TResult = 
    //force them to give you a good number
    let inline doer parsed : 'TLocal =
        if fst parsed then
            snd parsed //return the parsed value
        else //the parse failed
            Console.WriteLine "Enter a real number"
            let newInput = Console.ReadLine()
            unbox<'TLocal> (checkinput newInput)
    //do it slightly differently based on whether we want an int or double
    if typeof<'TResult> = double then
        unbox<'TResult> (doer (Double.TryParse input))
    else 
        unbox<'TResult> (doer (Int32.TryParse input))

就是说,现在它变得有点难看了,所以我可能不想使用这种代码。没有这个你可能会有更多的重复,但是有一点重复,你可能可以以更易读的方式解决你原来的问题。

在查看了 Tomas 的回答后,我能够以不同的方式编译它,使用 box 然后 unbox。

let double = Type.GetType("System.Double")
let int32 = Type.GetType("System.Int32")

//takes the user input and forces them to enter a valid number
// can return either valid doubles or ints
let rec checkinput (input) (msType:Type)  = 
    //force them to give you a good number
    let ConsoleTrap (parsed: obj) (msType :Type)=
        let parsed = unbox parsed
        if fst parsed then
            snd parsed //return the parsed value
        else
            Console.WriteLine "Enter a real number"
            let newInput = Console.ReadLine()
            checkinput newInput msType
    //do it slightly differently based on whether we want an int or double
    if msType = double then
        let parsed = Double.TryParse input
        ConsoleTrap (box parsed) double

    else 
        let parsed = Int32.TryParse input
        ConsoleTrap (box parsed) int32

我决定试一试并以两种不同的方式结束。
第一个依赖于“duck typing”。

let inline checkInput input =
  let parsed = ref Unchecked.defaultof<_>

  let rec aux input =
    if (^a : (static member TryParse : string * ^a byref -> bool) (input, &parsed.contents)) then
      !parsed
    else
      Console.WriteLine "Enter a real number"
      aux (Console.ReadLine ())

  aux input

// usage
let n = checkInput<int> (Console.ReadLine ())
// let n : int = checkInput (Console.ReadLine ()) // equivalent syntax
let d = checkInput<double> (Console.ReadLine ())
// works for any type with a matching TryParse member
let dt = checkInput<DateTime> (Console.ReadLine ())

第二个依赖discriminated union.

type ParseType = IntType | DoubleType
type ParseResult = IntResult of int | DoubleResult of double

// (note "input" arg could be η-converted)
let checkInput pType input =
  let rec aux parser resultCtor input =
    match parser input with
      true, parsed -> resultCtor parsed
    | _ ->
      Console.WriteLine "Enter a real number"
      aux parser resultCtor (Console.ReadLine ())

  // use the function associated with given "type case"
  match pType with
    IntType    -> aux Int32.TryParse IntResult input
  | DoubleType -> aux Double.TryParse DoubleResult input

// usage
let n = checkInput IntType (Console.ReadLine ())
let d = checkInput DoubleType (Console.ReadLine ())