Настройка диагностических сообщений об ошибках в больших проектах Mathematica
Всякий раз, когда я создаю большой проект Mathematica, я сталкиваюсь с этой проблемой: предотвращение лавины ошибок времени выполнения в Mathematica, то есть сообщения об ошибках Mathematica являются непрозрачными, архаичными и легионными.
Идея заключается в том, чтобы отключить все собственные сообщения об ошибках Mathematica и реализовать собственные проверки типов и сообщения об ошибках в каждой функции и модуле. Однако я не нашел простого и эффективного способа сделать это, и в итоге, например, какая-то функция генерирует вызов функции error 20 deep и затем получает целый каскад сообщений об ошибках вплоть до основной процедуры.
Как бы вы установили простой механизм для этого, который генерирует только одно сообщение об ошибке в функции, которая испытывает ошибку, и простой список цепочки вызовов функций?
РЕДАКТИРОВАТЬ: так как он пришел в пару ответов; Я специально искал что-то более легкое в отношении выходных данных, которые он производит (в противном случае я мог бы просто придерживаться сообщений об ошибках Mathematica), и, очевидно, также облегченное в вычислительных затратах. Так что пока Stack
а также Trace
Это определенно легкая накладная нагрузка, их вывод в сложных проектах анализировать не так быстро, и необходимо сделать некоторую работу, чтобы упростить его.
5 ответов
ЯСИ - Еще одна (глупая?) Идея...
Перечитайте ваш вопрос...
Идея заключается в том, чтобы отключить все собственные сообщения об ошибках 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
вывод... Прерывает при первом сообщении и оставляет "разумный" след стека. "Разумный" означает "Должен быть улучшен".
Но это совершенно не навязчиво!
Предложение для извлечения стека, может быть, что-то, что полагается на 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]]
Чтобы ответить на вопрос Самсдрама, код ниже (также от Криса) дает дерево оценки выражения Mathematica. Вот пост от MathGroup с исходным кодом и примерами.
(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]]
Одна попытка реализовать идею @ Тимо (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
Чтобы заставить мяч катиться здесь - одна идея, с которой я играю; создание псевдо-стека.
Сначала создайте глобальную переменную theStack={}
а потом в каждом Function
или же Module
начать с AppendTo[theStack,"thisFuncName"]
и заканчивается theStack=Most@theStack
, Предполагая умеренную (~ несколько десятков) глубину вызовов функций, это не должно добавить значительных накладных расходов.
Затем реализуйте свой собственный набор текста / проверку ошибок и используйте Print@theStack;Abort[];
на ошибки.
Уточнения этого метода могут включать:
- Выяснить способ динамического получения thisFuncionName, чтобы
AppendTo[]
может быть превращен в идентичный вызов функции для всехFunctions
а такжеModule
, - С помощью
Message[]
ВместоPrint[]
, - Перемещение других важных переменных / информации о состоянии на
theStack
,
Возможно, мы перестали думать об этом. Что, если мы просто немного подправим соответствие шаблонов аргументам. Например, если мы изменили функцию, чтобы проверить числовое значение, и добавили некоторый код, чтобы вывести ошибку в случае сбоя. Например,
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 дважды, один раз для проверки типа и один раз для реального. Это может быть плохо, если задействован дорогой вызов функции.
Я не нашел пути решения этой второй проблемы и хотел бы получить предложения по этому вопросу. Являются ли продолжения выходом из этой проблемы?
Надеюсь это поможет.