需要帮助的异常处理


4

我已阅读文档为CatchThrow,和朋友多次,我承认,我觉得他们完全不知所云......

谁能告诉我下面的Python的人会口片段Mathematica?

 
def safeeval(fn, x, epsilon=sys.float_info.epsilon): 
  try: 
   # first attempt 
   return fn(x) 
  except Exception, first_exception: 
   try: 
   try: 
    # second attempt 
    return fn(x + epsilon) 
   except: 
    # third (and last) attempt 
    return fn(x - epsilon) 
   except: 
   # give up! 
   raise first_exception 

在口头上:返回fn(x)fn(x)评价过程中捕捉任何异常。如果发生异常,请将其缓存在first_exception中,并返回fn(x + epsilon),再次捕获所有异常。如果再次出现异常(第二个),请返回fn(x - epsilon)。最后,如果再次出现异常(第三个),请放弃并重新提升first_exception

更高级别的,上面少直译应该是这样:返回没有错误评估的fn(x)第一个,fn(x + epsilon),并fn(x - epsilon)沉默那些沿途出现的任何错误)。如果这些表达式中没有一个没有错误地进行评估,那么请按照fn(x)的常规评估(发出相应的错误消息等)进行。


编辑:好的,我终于想出了一个解决方案,近似于什么上述我,虽然我不知道它的任何接近最佳:

safeEval[fn_, x_, epsilon_:$MachineEpsilon]:= Module[{v, sentinel}, 
  v = Quiet[Check[fn[x], 
    Check[fn[x+epsilon], 
     Check[fn[x-epsilon], 
      sentinel]]]]; 
  If[v =!= sentinel, v, fn[x]] 
] 

我(*啊哈* )全部抛弃CatchThrow命名。 (这些功能在我看来似乎是非常不自然地加到Mathematica上的东西,使它看起来更像其他更广为人知的语言。)

5

我不知道Python,并且我没有得到Python代码的一个方面;但这就是最后一段描述的内容。

safeeval[fn_, x_, epsilon_] := 
ReleaseHold @ Catch @ Quiet @ 
  Check[ 
  fn[x], 
  Check[ 
   ReleaseHold @ Catch @ Quiet @ 
   Check[ 
   fn[x + epsilon], 
   Throw @ Hold[fn[x - epsilon]]], 
   Throw @ Hold[fn[x]]]] 

我不知道如何重新进入从上Throw点的第一f[x],而无需创建一个无限循环。这是我不明白的Python代码的一部分:重新提高first_exception。 Python当然看起来更容易。也许有人会想出一个更优雅的解决方案。

例子

第一次尝试成功

safeeval[# &, 0, 1] 
(* 0 *) 

第二个尝试成功

safeeval[1/# &, 0, 1] 
(* 1 *) 

第三次尝试成功

safeeval[1/(# (# - 1)) &, 0, 1] 
(* 1/2 *) 

默认情况下 - 都以失败告终

safeeval[1/0 &, 0, 1] 

电源:: INFY:无限表达1/0遇到。>>

​​

编辑:Cacheing错误消息

应用的答案之一this question,可以很容易地适应上述从第一评价缓存错误信息f[x]并避免必须评估两次。

Module[{messages = {}}, 
  clearMessages[] := messages = {}; 
  collectMessages[m_] := AppendTo[messages, m]; 
  printMessages[] := ReleaseHold @ messages; 
  ]; 
safeeval[fn_, x_, epsilon_ : $MachineEpsilon] := 
Module[{retval}, 
  ReleaseHold @ Catch @ Quiet @ 
  Check[ 
   clearMessages[]; 
   Internal'AddHandler["Message", collectMessages]; 
   retval = fn[x]; 
   Internal'RemoveHandler["Message", collectMessages]; 
   retval, 
   Check[ 
   ReleaseHold @ Catch @ Quiet @ 
   Check[ 
   fn[x + epsilon], 
   Throw @ Hold[fn[x - epsilon]]], 
   Throw[Hold[printMessages[]; retval]] 
   ] 
   ] 
  ] 
+2

我认为这将更加符合Python代码一致,如果它被写'safeeval [FN_的, x_,epsilon _:$ MachineEpsilon]:= ...' 25 8月. 132013-08-25 22:18:00

  0

我想到了这一点,但由于我不知道Python,我不想承担什么'epsilon'。我希望OP知道'$ MachineEpsilon'。不过谢谢! (对评论+1。:) 25 8月. 132013-08-25 22:41:42

  0

谢谢!我提出了一个与你的解决方案非常相似的解决方案(请参阅我的编辑),尽管我无法遵循使用'Hold','ReleaseHold'等方法。我想他们的角色与保留整个'Catch '''抛出'计划?正如我在编辑中提到的那样,这个方案看起来像是基于公关目的而成为Mathematica的东西...... 25 8月. 132013-08-25 22:45:54

+1

@kjo'Hold'使'f [x]'不在'Quiet'内被评估。 'Catch'后'ReleaseHold'移除'Hold';在那个时候,'f [x]'将在'Quiet'之外被评估。 ==你的解决方案似乎是合理的。我们两个人的一个缺陷(IMO)是在最坏的情况下对f [x]进行两次评估。 25 8月. 132013-08-25 23:40:11

  0

@ MichaelE2:避免重新计算'f [x]'很简单(只需要将一个缓存模块变量赋值给这个评估结果的简单替换[f] [x] Quiet [检查[备忘录= f [x],检查[...]]]')我不知道如何做的是忠实地重复在第一次评估'f [x] '。最后,这就是为什么我要重复评估'f [x]'的原因。 26 8月. 132013-08-26 02:56:10

+1

@kjo查看更新。 (评论的第二部分是我评论f [x]'的两次意思。) 26 8月. 132013-08-26 03:36:19


3

看起来你已经有你的解决方案,而且是一个相当完美的一个,但也许是什么但缺乏的是这并不需要手动编写嵌套结构的便利方法。

我们可以使用递归。其基本形式是这样的:

SetAttributes[errorTry, HoldAll] 

errorTry[a_, b__] := Quiet @ Check[a, errorTry[b]] 

errorTry[x_] := x 

你会使用这样的:

fn[1] := (1/0; 1) 
fn[2] := (1/0; 2) 
fn[3] := (1/0; 3) 

errorTry[fn[1], fn[2], fn[3], fn[1]] 
1 

有了这个简单的表格你必须指定两次第一表达,它在跌倒情况下也会被评估两次。为了解决这些问题,我们可以使用:

SetAttributes[errorTry2, HoldAll] 

errorTry2[a_, b___] := Module[{x}, errorTry[x = a, b, x]] 

用途:

errorTry2[fn[1], fn[2], fn[3]] 

如果你想在fn[1]返回错误消息落空,你既可以简单地重新评估的表达,或者如果是代价高昂,你可以缓存。简单的方法看起来像这样:

SetAttributes[{errorTry3, eT3}, HoldAll] 

errorTry3[a_, b___] := eT3[a, b] /. eT3[] :> a 

eT3[a_, b___] := Quiet @ Check[a, eT3[b]] 

如上所述使用。最后,具有缓存功能的完整自包含功能:

SetAttributes[errorTry4, HoldAll] 

errorTry4[a_, b___] := 
    Module[{f, x, msg = {}}, 
    SetAttributes[f, HoldAll]; 
    f[i_, j___] := Quiet @ Check[i, f[j]]; 
    Block[{Message = AppendTo[msg, {##}] &}, x = a]; 
    If[msg === {}, x, f[b] /. f[] :> (Message @@@ msg; x)] 
    ] 

我希望这能为您提供所需的所有选项。

  0

回复中有很多很酷的想法。谢谢! 26 8月. 132013-08-26 13:02:28

  0

@kjo不客气。 26 8月. 132013-08-26 15:45:22


0

这是一个简单的Java实现的try/catch /终于,翻译在尝试/失败/最后

(* Mathematica Package *) 


BeginPackage["Language'"] 
(* ----------------------------------------------------------------------- *) 
(* 
  Try/Failure/Finally 
  Failed/ $Return/$ Failure 

  Try[ 
   body, 
   Failure[form, cbody], 
   ... 
   Finally[fbody] 
  ] 

where 

  Failed[tag] == Throw[ $Failed, tag] 

  Failed[c::m, args___] == Throw[$ Failed, Hold[c::m, {args}]] 

  It is possible modify the result generated in 'body' by assign a new 
  value to the symbol ' $return'. 

  With 'Throw[result, tag]', the result of the expression is 'result' 
  (o '$ Failed' if you use 'Failed[tag]'). 
   The value 'result' will be assigned to ' $Return' 

  Try[]   == Try[Null] 
  Try[body, Failure[...]*, Finally[...]?] 

  Failure[]  == Failure[_, Null] 
  Failure[form]  == Failure[form, Null] 
  Failure[form, ebody] 

  Finally[]  == Finally[Null]  
  Finally[fbody] 

  form: a pattern 
  tag: a expression 
  body, ebody, fbody: a expression 

  La valutazione procede come segue: 

  1) viene valutato 'body' (Try[body, ...]) 
  2) se non ci sono eccezioni, il risultato viene assegnato a$ Return 
   3) se e' presente la sezione 'Finally[fbody]' viene valutato 'fbody' 
   4) - se in 'fbody' non viene assegnato un nuovo valore a ' $Return' e ritorna 
   Null, allora il risultato e' il corrente valore di$ Return 
   - se in 'fbody' viene assegnato un nuovo valore a ' $Return' e ritorna 
   Null, allora il risultato e' il nuovo valore di$ Return 
   - se 'fbody' ritorna un valore diverso da Null, allora questo e' il nuovo 
   valore dell'espressione 
   5) in caso di eccezione (generata con 'Throw[value, tag]' oppure 'Failure[tag]' 
   che e' equivalente a 'Throw[ $Failed, tag]'), il valore di '$ Return' e' 
   'value' o ' $Failed'. 
  6) vengono scandite le sezioni 'Failure[form, cbody]' alla ricerca della 
   prima in cui 'form' ha un match con 'tag': 
   - se non viene trovata, l'eccezione viene propagata ma VIENE valutata la 
   sezione 'Finally[fbody]' se presente 
   - se viene trovata, viene valutato 'cbody', che segue le stesse regole 
   di 'fbody' 
   - se e' presente la sezione 'Finally[fbody]' la valutazione procede 
   come indicato al punto 4 
  7) l'esatta eccezione sollevata (tag) si puo' trovare nei blocchi 'Failure[]' 
   e 'Finally[]' nella variabile '$ Failure' 

  Le variabili ' $Failure' e '$ Return' usano 'dynamic scoping' ('Block[...]') 

  In 'ebody' e' possibile utilizzare eventuali pattern definiti in 'form' 
*) 
(* ----------------------------------------------------------------------- *) 

 $Return::usage="Usable in 'body' on 'Failure[]' and 'Finally[]' to change the \
  return value, but 'body' MUST return 'Null'. Otherwise the new value returned \
  is the value of the body evaluation";$ Failure::usage="Contains the exception generated"; 

Try::usage="Try[body, Failure[form, ebody]*, Finally[fbody]?] execute 'body' and \ 
  'fbody' if present. 
  If 'body' generate a exception (using Throw[.., tag]) the next expression \ 
  evaluated is the first 'ebody' of the 'Failure[form, ebody]' list where 'form' \ 
  match 'tag'"; 

Failed::usage="Failed[tag] == Throw[ $Failed, tag]\
   Failed[c::m, args___] === Throw[$ Failed, Hold[c::m, {args}]]"; 

Failure::usage="Failure[tag, ebody] | Failure[tag] | Failure[]" 

Finally::usage="Finally[fbody] | Finally[]"; 


(* ----------------------------------------------------------------------- *) 
(*          *) 
(* ----------------------------------------------------------------------- *) 

Begin["'Private'"] 

Unprotect[ 
  Try, Failure, Finally, 
  Failed, $Return,$ Failure 
]; 

(* ----------------------------------------------------------------------- *) 
(* 
  Try[]  == Try[Null] 
  Try[body, Failure[...]*, Finally[...]?] 

  Failure[]  == Failure[_, Null] 
  Failure[form] == Failure[form, Null] 
  Failure[form, ebody] 

  Finally[]  == Finally[Null]  
  Finally[fbody] 

  form: un pattern 
  tag: un'espressione 
*) 
(* ----------------------------------------------------------------------- *) 

SetAttributes[Try, HoldAll]; 
SetAttributes[Failed, HoldFirst]; 
SetAttributes[HandleFailures, HoldAll]; 


(* ----------------------------------------------------------------------- *) 
(* Failed         *) 
(* ----------------------------------------------------------------------- *) 

Failed[fail_] := Throw[ $Failed, Evaluate[fail]]; 

Failed[message:MessageName[category_, _], args___] := 
  With[{mh = Hold[message,{args}]}, 
   Message[message, args]; 
   Throw[$ Failed, mh]; 
  ]; 


(* ----------------------------------------------------------------------- *) 
(* Try         *) 
(* ----------------------------------------------------------------------- *) 

Try[body_:Null, Longest[failures___Failure], finally_Finally:Finally[]] := 
  Block[{ $Return=Null,$ Failure=Null}, 
   Catch[ 
   HandleBody[body], 
   _, 
   HandleFailures[#1, #2, Hold[failures]]& 
   ]; 
   HandleFinally[finally]; 
   Return[ $Return]; 
  ]; 


(* ----------------------------------------------------------------------- *) 
(* HandleBody        *) 
(* ----------------------------------------------------------------------- *) 

HandleBody[body_] := 
  With[{},$ Return=Evaluate[body] 
  ]; 


(* ----------------------------------------------------------------------- *) 
(* HandleFailures        *) 
(* ----------------------------------------------------------------------- *) 

(* 
  Param failures: Hold[failure, ...] 

  failure ::= Failure[]  == Failure[_, Null] 
    Failure[form] == Failure[form, Null] 
    Failure[form, body] 
*) 
HandleFailures[return_, tag_, failures_]:= 
  Module[{i, n=Length[failures]}, 
   $Return=return;$ Failure=tag; 
   For[i=1, i<=n, ++i, 
   With[{failure=failures[[i]]}, 
   If[Length[failure]==0, Return[ $Return]]; (*Failure[]*) 
   With[{form=failure[[1]]},  (*Failure[form, body?]*) 
   If[MatchQ[tag, form], 
    Return[HandleFailure[tag, failure]]; 
   ]]]; 
   ]; 
   Throw[return, tag]; 
  ]; 

(* 
  Failure[form] == Failure[form, Null] 
  Failure[form, body] 
*) 
HandleFailure[tag_, Failure[form_, body_:Null]] := 
  With[{return=tag/.form->body}, 
   If[return =!= Null,$ Return=return]; 
   Return[ $Return]; 
  ]; 


(* ----------------------------------------------------------------------- *) 
(* HandleFinally        *) 
(* ----------------------------------------------------------------------- *) 

(* 
  Finally[] == Finally[Null] 
  Finally[body] 
*) 
HandleFinally[Finally[body_:Null]] := 
  With[{return=Evaluate[body]}, 
   If[return =!= Null,$ Return=return]; 
   Return[ $Return]; 
  ]; 


(* ----------------------------------------------------------------------- *) 
(*          *) 
(* ----------------------------------------------------------------------- *) 

Protect[ 
  Try, Failure, Finally, 
  Failed,$ Failure, $Return 
]; 

End[] 

Print["Language'TryCatch' Installed"] 

EndPackage[]