我相信我对在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
Run Code Online (Sandbox Code Playgroud)
我很乐意用血淋淋的细节来解释代码,但基本上我想查看字符串是否在列表中重复.如果我发现重复的字符串,我会引发异常.注意我正在函数check_pat中处理异常,它调用函数my_g.但是,当我使用一些测试方法运行代码时,我得到未捕获的异常NoAnswer
我可以捕获另一个(调用)函数中的一个函数抛出的异常吗?我究竟做错了什么?
Andreas和未来观众的其他细节.最初的提示是首先展开结构并获取字符串列表,然后才能查看重复项.我觉得效率很低,最好在你展开的时候寻找重复的东西.不幸的是,我的SML知识不足以提出一个超级干净的解决方案.真的,我不关心my_g的返回值.如果它没有抛出异常,则没有重复.就如此容易.但似乎语法规则迫使我检查返回值.既然你已经为我解决了"句柄"问题,我可能会重新审视这个问题.我希望只写:
(my_g [] p
true)
handle NoAnswer => false
Run Code Online (Sandbox Code Playgroud)
但这似乎不起作用.更广泛地说,虽然我认为我的解决方案比首先展开整个列表只是为了寻找重复项更有效,但我怀疑使用像我这样的例外的想法并不是好的风格.在我熟悉的语言(C++,C#)中,异常意味着发生了一些异常或意外.找到一个重复的字符串肯定不是特例.同样,我确信还有另一种方法可以在不使用异常的情况下停止第一个副本.我只是不够精通SML才能知道它.谢谢!
这只是一个括号内容的问题:handle绑定比紧if,所以你已经有效地写了
if ... then ... else (... handle ...)
Run Code Online (Sandbox Code Playgroud)
相反,你想要
(if ... then ... else ...) handle ...
Run Code Online (Sandbox Code Playgroud)
所以你需要加括号.
顺便说一句,我无法理解你的使用if- 为什么在两个分支产生相同结果时有条件?而且,这if A then true else B是一种冗长的说法A orelse B.
编辑回复编辑问题:如果要忽略表达式的结果并返回其他内容,则可以使用分号运算符:
(my_g [] p; true)
Run Code Online (Sandbox Code Playgroud)
但是,通常不建议使用非异常控制流的异常.有一种更简洁的方法来编写这个函数:
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))
Run Code Online (Sandbox Code Playgroud)
编辑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 []))
Run Code Online (Sandbox Code Playgroud)
或者,如果您愿意使用一些可变状态:
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
Run Code Online (Sandbox Code Playgroud)