每当我创建一个大型的Mathematica项目时,我都遇到了这个问题:Preventing avalanche of runtime errors in Mathematica,即Mathematica的错误信息是不透明的,过时的和军团。
然后,我们的想法是禁用所有Mathematica自己的错误消息,并在每个功能和模块中实现您自己的类型检查和错误消息。但是我还没有找到一种简单而有效的方法来完成这个并最终得到,例如,一些函数生成错误20函数调用深度,然后获得整个级联的错误消息一直回到主例程。
如何为此设置一个简单的机制,只在遇到错误的函数和函数调用链的简单列表中生成一条错误消息?
编辑:因为它出现了几个答案;我特意在寻找关于它产生的输出的轻量级(否则我可以坚持使用Mathematica的错误消息),并且显然在计算开销方面也是轻量级的。因此虽然Stack
和Trace
肯定对开销很轻,但它们在复杂项目中的输出并不能快速解析,需要完成一些工作来简化它。
答案 0 :(得分:9)
重新阅读你的问题...
然后,我们的想法是禁用所有Mathematica自己的错误消息,并在每个功能和模块中实现您自己的类型检查和错误消息。 的
发现这个:
$MessagePrePrint = ( #; Print[Stack[_][[;; -5]]]; Abort[]) &
v[x_, y_] := w[x, y];
w[x_, y_] := x/y;
StackComplete@v[1, 0];
During evaluation of In[267]:= {StackComplete[v[1,0]];,
StackComplete[v[1,0]], v[1,0], w[1,0], 1/0, 1/0, Message[Power::infy,1/0]}
Out[267]= $Aborted
结论......首先中止消息并留下“合理的”堆栈跟踪。 “合理”是指“应该改进”。
但这完全是非侵入性的!
答案 1 :(得分:3)
让球在这里滚动是一个我一直在玩弄的想法;创建一个伪堆栈。
首先创建一个全局变量theStack={}
,然后在每个Function
或Module
中以AppendTo[theStack,"thisFuncName"]
开头,以theStack=Most@theStack
结尾。假设中等(〜几十)深度的函数调用,这不应该增加任何显着的开销。
然后实施您自己的输入/错误检查,并在错误时使用Print@theStack;Abort[];
。
这种方法的改进包括:
AppendTo[]
可以对所有Functions
和Module
进行相同的函数调用。Message[]
代替Print[]
。theStack
上推送其他重要变量/有状态信息。答案 2 :(得分:3)
提取堆栈的建议,可能依赖于Trace?
Chris Chiasson在下面使用Trace的一个例子。此代码将1 + Sin [x + y] + Tan [x + y]的评估树保存到〜/ temp / msgStream.m
Developer`ClearCache[];
SetAttributes[recordSteps, HoldAll];
recordSteps[expr_] :=
Block[{$Output = List@OpenWrite["~/temp/msgStream.m"]},
TracePrint[Unevaluated[expr], _?(FreeQ[#, Off] &),
TraceInternal -> True];
Close /@ $Output;
Thread[
Union@Cases[
ReadList["~/temp/msgStream.m", HoldComplete[Expression]],
symb_Symbol /;
AtomQ@Unevaluated@symb &&
Context@Unevaluated@symb === "System`" :>
HoldComplete@symb, {0, Infinity}, Heads -> True],
HoldComplete]
];
recordSteps[1 + Tan[x + y] + Sin[x + y]]
要回答Samsdram的问题,下面的代码(也来自Chris)给出了Mathematica表达式的评估树。 Here is the post from MathGroup with source code and examples.
(Attributes@# = {HoldAllComplete}) & /@ {traceToTreeAux, toVertex,
HoldFormComplete, getAtoms, getAtomsAux}
MakeBoxes[HoldFormComplete[args___], form_] :=
MakeBoxes[HoldForm[args], form]
edge[{head1_, pos1_, xpr1_}, {head2_, pos2_, xpr2_}] :=
Quiet[Rule[{head1, vertexNumberFunction@pos1, xpr1}, {head2,
vertexNumberFunction@pos2, xpr2}], {Rule::"rhs"}]
getAtomsAux[atom_ /; AtomQ@Unevaluated@atom] :=
Sow[HoldFormComplete@atom, getAtomsAux]
getAtomsAux[xpr_] := Map[getAtomsAux, Unevaluated@xpr, Heads -> True]
getAtoms[xpr_] := Flatten@Reap[getAtomsAux@xpr][[2]]
toVertex[traceToTreeAux[HoldForm[heldXpr_], pos_]] := toVertex[heldXpr]
toVertex[traceToTreeAux[HoldForm[heldXprs___], pos_]] :=
toVertex@traceToTreeAux[Sequence[], pos]
(*this code is strong enough to not need the ToString commands,but \
some of the resulting graph vertices give trouble to the graphing \
routines*)
toVertex[
traceToTreeAux[xpr_, pos_]] := {ToString[
Short@Extract[Unevaluated@xpr, 0, HoldFormComplete], StandardForm],
pos, ToString[Short@First@originalTraceExtract@{pos}, StandardForm]}
traceToTreeAux[xpr_ /; AtomQ@Unevaluated@xpr, ___] := Sequence[]
traceToTreeAux[_HoldForm, ___] := Sequence[]
traceToTreeAux[xpr_, pos_] :=
With[{lhs = toVertex@traceToTreeAux[xpr, pos],
args = HoldComplete @@ Unevaluated@xpr},
Identity[Sequence][
ReleaseHold[
Function[Null, edge[lhs, toVertex@#], HoldAllComplete] /@ args],
ReleaseHold@args]]
traceToTree[xpr_] :=
Block[{vertexNumber = -1, vertexNumberFunction,
originalTraceExtract},
vertexNumberFunction[arg_] :=
vertexNumberFunction[arg] = ++vertexNumber;
originalTraceExtract[pos_] :=
Extract[Unevaluated@xpr, pos, HoldFormComplete]; {MapIndexed[
traceToTreeAux, Unevaluated@xpr, {0, Infinity}]}]
TraceTreeFormPlot[trace_, opts___] :=
Block[{$traceExpressionToTree = True},
Through@{Unprotect, Update}@SparseArray`ExpressionToTree;
SparseArray`ExpressionToTree[trace, Infinity] = traceToTree@trace;
With[{result = ToExpression@ToBoxes@TreeForm[trace, opts]},
Through@{Unprotect, Update}@SparseArray`ExpressionToTree;
SparseArray`ExpressionToTree[trace, Infinity] =.;
Through@{Update, Protect, Update}@SparseArray`ExpressionToTree;
result]];
TraceTreeFormPlot[Trace[Tan[x] + Sin[x] - 2*3 - 55]]
答案 3 :(得分:3)
尝试实施@Timo的想法(theStack)
不完整且可能存在缺陷,但只是为了继续思考:
Clear["Global`*"];
funcDef = t_[args___] \[CircleMinus] a_ :>
{t["nude", args] := a,
ReleaseHold[Hold[t[args] :=
(If[! ValueQ[theStack], theStack = {}];
AppendTo[theStack, ToString[t]];
Check[ss = a, Print[{"-TheStack->", Evaluate@theStack}];
Print@Hold[a]; Abort[]];
theStack = Most@theStack;
Return[ss])
]]};
v[x_, y_]\[CircleMinus] (Sin@ g[x, y]) /. funcDef;
g[x_, y_]\[CircleMinus] x/y /. funcDef;
v[2, 3]
v[2, 0]
输出:
Out[299]= Sin[2/3]
During evaluation of In[295]:= Power::infy: Infinite expression 1/0 encountered. >>
During evaluation of In[295]:= {-TheStack->,{v,g}}
During evaluation of In[295]:= Hold[2/0]
Out[300]= $Aborted
答案 4 :(得分:2)
也许我们一直在想这个。如果我们只是稍微调整参数上的模式匹配会怎么样?例如,如果我们修改函数以检查数字数量并添加一些代码以在错误时打印错误。例如,
TypeNumeric[x_] := If[! NumericQ[Evaluate[x]],
Print["error at "]; Print[Stack[]]; Print["Expression "]; Print[x]; Print["Did
not return a numeric value"];Return[False],
(*Else*)
Return[True];]
SetAttributes[TypeNumeric, HoldAll];
步骤2:如果你有一个函数,f [x_]需要一个数字量,只需用标准模式测试写出来,一切都应该很好
Input:
f[x_?TypeNumeric] := Sqrt[x]
f[Log[y]]
f[Log[5]]
Output:
error at
{f}
Expression
Log[y]
Did not return a numeric value
f[Log[y]]
Sqrt[Log[5]]
我相信这会起作用,它使得强大的类型检查就像编写一个或两个函数一样简单。问题是这可能是非常低效的,因为这段代码评估表达式x两次,一次用于类型检查,一次用于实际。如果涉及昂贵的函数调用,这可能会很糟糕。
我还没有找到解决第二个问题的方法,并欢迎在这方面提出建议。延续是解决这个问题的方法吗?
希望这有帮助。