Как найти строку, где произошла ошибка в ноутбуке 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 на своем веб-сайте. Я не пробовал это сам, но я знаю Дэвида как очень способного специалиста по математике, поэтому я надеюсь, что это должно быть хорошо.