SML将字符串转换为整数并捕获错误

3

我想把一个字符串转换成整数,并对其进行错误捕获。如果发生错误,我也想知道在哪里放置处理错误的代码。

我知道如何进行转换,但不确定如何捕获错误和错误后代码会跳到哪里。

我相信转换的方法是 Int.fromString(x)。

谢谢。

2个回答

3
SML有两种处理错误的方法。一种是基于 raise 抛出错误和 handle 捕捉错误,与Python或Java等语言中的错误处理方式有些相似。这种方法是有效的,但导致的代码往往会失去一些功能性。另一种方法是基于“选项”的概念。由于 Int.fromString 的返回类型是
string -> int option

使用基于选项的方法是最有意义的。

int option 要么是 SOME n,其中 n 是整数,要么就是 NONE。函数 Int.fromString 如果在尝试将字符串转换为整数时失败,则返回后者(NONE)。调用 Int.fromString 的函数可以显式地测试是否为 NONE,并在返回的值是 SOME n 形式时使用 valOf 提取值。或者,更符合惯例的做法是,在 case 表达式中使用模式匹配。这里有一个玩具示例:

fun squareString s = 
    case Int.fromString(s) of
        SOME n => Int.toString (n * n) |
        NONE => s ^ " isn't an integer";

此函数的类型为 string -> string。典型输出如下:
- squareString "4";
val it = "16" : string
- squareString "Bob";
val it = "Bob isn't an integer" : string

请注意,以NONE =>开头的子句基本上是一个错误处理程序。如果您定义的函数无法处理此类错误,则可能会出现问题。例如:
fun squareString s = 
    case Int.fromString(s) of
        SOME n => SOME (Int.toString (n * n))|
        NONE => NONE;

这是一个类型为string -> string option的内容,输出现在看起来像这样:
- squareString "4";
val it = SOME "16" : string option
- squareString "Bob";
val it = NONE : string option

这将使呼叫者负责确定如何处理该选项。

2
John所解释的错误处理方法在StackOverflow问题'无需case语句拆包SML DataType中的数据'中有详细阐述。那里的用例有些不同,因为它还涉及语法树,但对于较小的情况也适用相同的便利性:
fun squareString s = Int.fromString s >>= (fn i => SOME (i*i))

假设你定义了 >>= 运算符为:
infix 3 >>=
fun NONE >>= _ = NONE
  | (SOME a) >>= f = f a

使用“a选项”进行错误处理的缺点是,每次使用具有此返回类型的函数时,您都必须考虑它是否出错。这并不是不合理的。这就像强制性的空值检查。但是,这样做的代价是无法轻松地组合您的函数(例如使用 o 运算符)以及大量嵌套的情况处理语句。
fun inputSqrt s =
    case TextIO.inputLine TextIO.stdIn of
         NONE => NONE
       | SOME s => case Real.fromString s of
                        NONE => NONE
                      | SOME x => SOME (Math.sqrt x) handle Domain => NONE

一种解决方法是,您可以将此常量错误处理程序构建到函数组合运算符中,只要所有函数共享相同的表达错误方式,例如使用「'a option」。
fun safeSqrt x = SOME (Math.sqrt x) handle Domain => NONE

fun inputSqrt () =
    TextIO.inputLine TextIO.stdIn >>=
      (fn s => Real.fromString s  >>=
      (fn x => safeSqrt x))

甚至可以通过应用Eta conversion更加简洁:
fun inputSqrt () = TextIO.inputLine TextIO.stdIn >>= Real.fromString >>= safeSqrt

该函数可能会失败,原因可能是输入不足、输入无法转换为“实数”或者输入是负数。自然地,这种错误处理不够智能,不能说明错误的具体原因,因此你可能需要将函数从使用“'a option”扩展到使用“('a,'b) either”。
datatype ('a, 'b) either = Left of 'a | Right of 'b

infix 3 >>=
fun (Left msg) >>= _ = Left msg
  | (Right a) >>= f = f a

fun try (SOME x) _ = Right x
  | try NONE msg = Left msg

fun inputLine () =
    try (TextIO.inputLine TextIO.stdIn) "Could not read from stdIn."

fun realFromString s =
    try (Real.fromString s) "Could not derive real from string."

fun safeSqrt x =
    try (SOME (Math.sqrt x) handle Domain => NONE) "Square root of negative number"

fun inputSqrt () =
    inputLine () >>= realFromString >>= safeSqrt

尝试一下这个:
- ​inputSqrt ();
​9
> val it = Right 3.0 : (string, real) either
- ​inputSqrt ();
​~42
> val it = Left "Square root of negative number" : (string, real) either
- ​inputSqrt ();
Hello
> val it = Left "Could not derive real from string." : (string, real) either
- (TextIO.closeIn TextIO.stdIn; inputSqrt ());
> val it = Left "Could not read from stdIn." : (string, real) either

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接