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

How do I catch Exception in SML from another function?

我相信我对在 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

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

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

谢谢,戴夫

为 Andreas 和未来的观众提供的更多详细信息。最初的提示是首先展开结构并获得字符串列表,然后才遍历并查找重复项。我觉得这样效率低下,最好在展开时查找重复项。不幸的是,我的 SML 知识还不足以提出一个超级干净的解决方案。真的,我不关心 my_g 的 return 值。如果它没有抛出异常,则没有重复项。就如此容易。但似乎语法规则迫使我检查 return 值。既然您已经为我解决了 "handle" 问题,我可能会重新考虑这个问题。我希望只写:

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

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

这只是括号的问题:handle 绑定比 if 更紧密,因此您有效地编写了

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

相反,你想要

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

所以你需要放在括号里。

顺便说一句,我无法理解您对 if 的使用——当两个分支产生相同结果时为什么要有条件?此外,if A then true else BA orelse B.

的一种冗长表达方式

Edit 回复问题中的编辑:如果你想忽略表达式的结果并 return 其他东西,那么你可以使用分号运算符:

(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