Облегченная функция отладки
В дополнение к другим предложениям, вот функция, которая помогла мне несколько раз:
ClearAll[debug];
SetAttributes[debug, HoldAll];
debug[code_] :=
Internal`InheritedBlock[{Message},
Module[{inMessage},
Unprotect[Message];
Message[args___] /; ! MatchQ[First[Hold[args]], _$Off] :=
Block[{inMessage = True},
Print[{
Shallow /@ Replace[#, HoldForm[f_[___]] :> HoldForm[f], 1],
Style[Map[Short, Last[#], {2}], Red]
} &@Drop[Drop[Stack[_], -7], 4]
];
Message[args];
Throw[$Failed, Message];
] /; ! TrueQ[inMessage];
Protect[Message];
];
Catch[StackComplete[code], Message]]
Это в основном переопределяет Message
временно ходитьвверх по стеку выполнения и распечатайте имена вызываемых функций в удобной для понимания форме, плюс последний вызов, который привел к сообщению об ошибке, и само сообщение об ошибке.После этого мы завершаем выполнение через исключение, чтобы не генерировались непонятные цепочки сообщений об ошибках.
Примеры использования
Вот как это работает на примере из ответа @ Mr.Wizard:
In[211]:= debug[myFunc2[Range@10,#1]]
During evaluation of In[211]:=
{{myFunc2,Pick,myFunc1,Part},{1,2,3,4,5,6,7,8,9,10}[[#1]]}
During evaluation of In[211]:= Part::pspec: Part specification #1 is neither
an integer nor a list of integers. >>
Out[211]= $Failed
(в блокноте проблемный вызов функции окрашен в красный цвет).Это позволяет быстро увидеть цепочку вызовов функций, которые приводят к проблеме.
Вот еще один пример: мы создаем пользовательскую функцию gatherBy
, которая собирает элементы в списке в соответствии с другим списком «меток», длина которого должна быть такой же, как и у оригинала:
listSplit[x_, lengths_] :=
MapThread[Take[x, {##}] &, {Most[#], Rest[#] - 1}] &@
Accumulate[Prepend[lengths, 1]];
gatherBy[lst_, flst_] :=
listSplit[lst[[Ordering[flst]]], (Sort@Tally[flst])[[All, 2]]];
Например:
In[212]:= gatherBy[Range[10],{1,1,2,3,2,4,5,5,4,1}]
Out[212]= {{1,2,10},{3,5},{4},{6,9},{7,8}}
Поскольку я намеренно оставил все проверки типов, вызовы с аргументами неправильных типов приведут к цепочке неприятных сообщений об ошибках:
In[213]:= gatherBy[Range[10],Range[15]]//Short
During evaluation of In[206]:= Part::partw: Part 11 of {1,2,3,4,5,6,7,8,9,10} does not exist. >>
(* 4 more messages here *)
Out[213]//Short= {{1,2,3,4,5,6,7,8,9,10},<<14>>}
Используя debug
, мы можем довольно быстро увидеть, что случилось:
In[214]:= debug[gatherBy[Range[10],Range[15]]]
During evaluation of In[214]:=
{{gatherBy,listSplit,Part},
{1,2,3,4,5,6,7,8,9,10}[[Ordering[{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}]]]}
During evaluation of In[214]:= Part::partw: Part 11 of {1,2,3,4,5,6,7,8,9,10} does not exist. >>
Out[214]= $Failed
Вызов gatherBy[Range[10], a]
с некоторым символом a
- это еще один пример, где помогает перенос debug
вокруг.
Применимость
Другие предложенные методы более систематичны и, возможно, более широко рекомендуются, но этот метод прост в применении и приводит к результатам, которые часто легче понять (например, по сравнению с результатамиTrace
, что не всегда легко прочитать).Однако я использовал его не так часто, чтобы гарантировать, что он всегда работает.