Как найти строку, где произошла ошибка в ноутбуке Mathematica?
У меня есть файл Mathematica с именем myUsefulFunctions.m, содержащий, например, функцию mySuperUsefulFunction. Предположим, что я вызываю mySuperUsefulFunction в блокноте и получаю следующую ошибку:
Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>
Есть ли способ найти строку в myUsefulFunctions.m, где произошла эта ошибка?
Ответы
Ответ 1
Я не знаю, как найти строку в файле, которая, как я полагаю, была прочитана без ошибок.
Однако вы можете использовать Trace
и связанные функции, чтобы увидеть, где в цепочке оценки произошла ошибка.
Пример:
myFunc1[x_, y_] := x[[y]]
myFunc2[a_List, n_] := Pick[a, a, myFunc1[a, n]]
myFunc2[[email protected], #1]
During evaluation of In[4]:= Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>
С Trace
:
myFunc2[[email protected], #1] // Trace // Column
{Range[10], {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}
myFunc2[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, #1]
Pick[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, myFunc1[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, #1]]
{myFunc1[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, #1], {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}[[#1]], {Message[Part::pspec, #1], {MakeBoxes[Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>, StandardForm], RowBox[{RowBox[{Part, ::, "pspec"}], : , "\!\(\*StyleBox[\"\\\"Part specification \\\"\", \"MT\"]\)\!\(\*StyleBox[\!\(#1\), \"MT\"]\)\!\(\*StyleBox[\"\\\" is neither an integer nor a list of integers.\\\"\", \"MT\"]\) \!\(\*ButtonBox[\">>\", ButtonStyle->\"Link\", ButtonFrame->None, ButtonData:>\"paclet:ref/message/General/pspec\", ButtonNote -> \"Part::pspec\"]\)"}]}, Null}, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}[[#1]]}
Вы можете видеть, что как раз перед тем, как вызывается Message[Part::pspec, #1]
, что приводит к большому беспорядку форматирования, мы имели:
{myFunc1[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, #1], {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}[[#1]]
Это показывает, что вызывается myFunc1[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, #1]
, и это вызывает ошибку {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}[[#1]]
, которая явно ошибочна.
Пожалуйста, смотрите этот вопрос и его ответы для более удобного использования Trace
:
https://stackoverflow.com/q/5459735/618728
Ответ 2
Легкая функция отладки
В дополнение к другим предложениям, здесь есть функция, которая помогла мне несколько раз:
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[[email protected],#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]]], ([email protected][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
, что не всегда легко читать). Я не использовал его так часто, чтобы гарантировать, что он всегда работает.
Ответ 3
Помимо отладчика в Workbench также есть отладчик, встроенный в Mathematica. Вы можете найти его в меню "Оценка". Это плохо документировано и довольно сложно/нетрадиционно, чтобы заставить его работать. Вот пошаговая инструкция, как его использовать:
Предполагая, что вы включили отладчик в меню "Оценка", ваша оконная строка укажет на это сеанс отладки, и у вас будет несколько палитр отладчика.
![enter image description here]()
Теперь выберите количество строк, которые вы хотите использовать в качестве точек останова, и нажмите на текст "break at selection". Точки останова будут отмечены красным контуром.
![enter image description here]()
и запустите код, нажав Shift-return и будьте готовы к небольшому разочарованию: он не работает. Похоже, вы не можете определить точки останова на уровне линии. Он должен быть на уровне функции. Кроме того, MMA довольно разборчиво относится к функциям, которые вы можете использовать. Функция Print
, по-видимому, не работает и при назначении. Тем не менее, Integrate
в этом примере, но вы должны выбрать его голову и обе скобки и сделать точку останова. Если вы это сделали, а затем выполните блок кода, вы получите следующее:
![enter image description here]()
Точка останова выделена зеленым цветом, некоторые дополнительные параметры в палитре управления доступны для управления дальнейшим потоком программы, и в окне стека есть выражения. Остальное более или менее похоже на стандартный отладчик. Обратите внимание, что вы можете вставлять точки останова, такие как Cos в Integrate. Для языка, который может иметь глубоко вложенные структуры, это важно.
Другим вариантом может быть отладчик Дэвида Бейли. Он предлагает бесплатный отладчик DebugTrace на своем веб-сайте. Я не пробовал это сам, но я знаю Дэвида как очень способного специалиста по математике, поэтому я надеюсь, что это должно быть хорошо.
Ответ 4
Вы можете использовать WolframWorkbench и отладчик:
http://www.wolfram.com/broadcast/screencasts/workbench/debugging/
вы можете установить точку прерывания и выполнить код.