如何从另一个函数中捕获SML中的Exception?

时间:2017-07-07 23:19:44

标签: sml smlnj

我相信我对在SML中捕获异常有一些基本的误解。

我写了以下代码:

    fun my_g acc p =
    let 
    val r =  my_g acc
    in
    case p of
        Wildcard          => acc 
      | Variable x        =>    if List.exists (fn y => y = x) acc then raise NoAnswer else x::acc 
      | TupleP ps         => List.foldl (fn (p,i) => (my_g i p)) acc  ps
      | ConstructorP(_,p) => r p
      | _                 => acc
    end

(* val check_pat = fn : pattern -> bool *)          
 fun check_pat p =
   if my_g []  p <> [] then
            true
else
    true      
 handle NoAnswer => false

我很高兴用血淋淋的细节解释代码,但基本上我正在查看字符串是否在列表中重复。如果我发现重复的字符串,我会引发异常。注意我正在函数check_pat中处理异常,它调用函数my_g。但是,当我使用一些测试方法运行代码时,我得到未捕获的异常NoAnswer

我可以捕获另一个(调用)函数中的一个函数抛出的异常吗? 我做错了什么?

谢谢,戴夫

Andreas和未来观众的其他细节。最初的提示是首先展开结构并获取字符串列表,然后才能查看重复项。我觉得效率很低,最好在你展开的时候寻找重复的东西。不幸的是,我的SML知识不足以提出一个超级干净的解决方案。真的,我不关心my_g的返回值。如果它没有抛出异常,则没有重复。就如此容易。但似乎语法规则迫使我检查返回值。既然你已经为我解决了“句柄”问题,我可能会重新审视这个问题。我希望只写:

(my_g [] p
true)
handle NoAnswer => false

但这似乎不起作用。更广泛地说,虽然我认为我的解决方案比首先展开整个列表只是为了寻找重复项更有效,但我怀疑使用像我这样的例外的想法并不是好的风格。在我熟悉的语言(C ++,C#)中,异常意味着发生了一些异常或意外。找到一个重复的字符串肯定不是特例。同样,我确信还有另一种方法可以在不使用异常的情况下停止第一个副本。我只是不够精通SML才能知道它。谢谢!

1 个答案:

答案 0 :(得分:5)

这只是括号内容的问题:handleif更紧密,所以你已经有效地写了

if ... then ... else (... handle ...)

相反,你想要

(if ... then ... else ...) handle ...

所以你需要加上括号。

顺便说一句,我无法理解你对if的使用 - 为什么在两个分支产生相同结果时有条件?此外,if A then true else B是一种冗长的说法A orelse B

编辑以回复编辑问题:如果要忽略表达式的结果并返回其他内容,则可以使用分号运算符:

(my_g [] p; true)

但是,通常不建议使用异常控制流程。有一种更简洁的方法来编写这个函数:

fun ids (Variable x)       = [x]
  | ids (Tuple ps)         = List.concat (List.map ids ps)
  | ids (Constructor(_,p)) = ids p
  | ids _                  = []

fun hasDups []      = false
  | hasDups (x::xs) = List.exists (fn y => y = x) xs orelse hasDups xs

fun checkPat p = not (hasDups (ids p))

编辑2:在正常情况下(没有重复项),此解决方案并不比另一种解决方案慢。所以它不一定值得采取捷径。但是,如果你坚持,有各种选择不需要例外。例如:

fun checkPat'(_, NONE)               = NONE
  | checkPat'(Variable x, SOME xs)   = if List.exists (fn y => y = x) xs then NONE else SOME (x::xs)
  | checkPat'(Tuple ps, xso)         = List.foldl checkPat' xso ps
  | checkPat'(Constructor(_,p), xso) = checkPat'(p, xso)
  | checkPat'(_, xso)                = xso

fun checkPat p = isSome (checkPat'(p, SOME []))

或者,如果您愿意使用一些可变状态:

fun checkPat' xs (Variable x)       = List.exists (fn y => y = x) (!xs) before (xs := x :: !xs)
  | checkPat' xs (Tuple ps)         = List.all (checkPat' xs) ps
  | checkPat' xs (Constructor(_,p)) = checkPat' xs p
  | checkPat' xs _                  = true

fun checkPat p = checkPat' (ref []) p