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

如何从另一个函数捕获SML中的异常?,sml,smlnj,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

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

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

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

if ... then ... else (... handle ...)
相反,你想要

(if ... then ... else ...) handle ...
所以你需要加上括号

顺便说一句,我无法理解您使用的
if
——当两个分支产生相同的结果时,为什么使用条件?另外,
如果A那么true else B
是表示
A或lse B
的冗长方式

Edit in reply to Edit in the question:如果要忽略表达式的结果并返回其他内容,则可以使用分号运算符:

(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

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

if ... then ... else (... handle ...)
相反,你想要

(if ... then ... else ...) handle ...
所以你需要加上括号

顺便说一句,我无法理解您使用的
if
——当两个分支产生相同的结果时,为什么使用条件?另外,
如果A那么true else B
是表示
A或lse B
的冗长方式

Edit in reply to Edit in the question:如果要忽略表达式的结果并返回其他内容,则可以使用分号运算符:

(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

你为你的异常声明了构造函数吗?Marco,是的,我声明了。我忘了把它放在问题里。很抱歉。它是:异常NoAnswer也可以,参见Andreas的答案。你为你的异常声明了构造函数吗?Marco,是的,我声明了。我忘了把它放在问题里。很抱歉。它是:例外也不回答,见安德烈亚斯的回答。非常感谢安德烈亚斯!我是SML的新手,对语法很难理解。我很清楚“两个分支产生相同的结果”,并对问题进行了解释(我需要输入代码,其他人可能会感兴趣)。相信我,我对这件事感到不好意思,但我想不出正确的方法:)再次感谢@Dave,还有一个显示替代解决方案的编辑:)谢谢Andreas。“;”是我没有尝试过的东西,尽管我应该告诉你它们在C#/C++中无处不在!我非常喜欢你的中间示例。至于“正常”的情况,我完全希望,如果你给我一份10000个项目的清单,即使你发誓没有,也会有重复的项目。此外,我仍然为自己感到骄傲,因为我考虑到在检查重复项之前不使用fold或map检查整个列表的可能性:)说真的,我确实想到,与list.exits相比,使用fold(或map)的行为在时间上是无关紧要的。如果是这样,那将是支持您的第一个示例的一个很好的理由。再次感谢!非常感谢安德烈亚斯!我是SML的新手,对语法很难理解。我很清楚“两个分支产生相同的结果”,并对问题进行了解释(我需要输入代码,其他人可能会感兴趣)。相信我,我对这件事感到不好意思,但我想不出正确的方法:)再次感谢@Dave,还有一个显示替代解决方案的编辑:)谢谢Andreas。“;”是我没有尝试过的东西,尽管我应该告诉你它们在C#/C++中无处不在!我非常喜欢你的中间示例。至于“正常”的情况,我完全希望,如果你给我一份10000个项目的清单,即使你发誓没有,也会有重复的项目。此外,我仍然为自己感到骄傲,因为我考虑到在检查重复项之前不使用fold或map检查整个列表的可能性:)说真的,我确实想到,与list.exits相比,使用fold(或map)的行为在时间上是无关紧要的。如果是这样,那将是支持您的第一个示例的一个很好的理由。再次感谢!