Как найти строку, где произошла ошибка в записной книжке Mathematica? - PullRequest
27 голосов
/ 03 декабря 2011

У меня есть файл Mathematica с именем myUsefulFunctions.m, содержащий, например, функцию mySuperUsefulFunction. Предположим, я вызываю mySuperUsefulFunction в записной книжке и получаю следующую ошибку:

Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>

Есть ли способ найти строку в myUsefulFunctions.m, где произошла эта ошибка?

Ответы [ 4 ]

21 голосов
/ 03 декабря 2011

Облегченная функция отладки

В дополнение к другим предложениям, вот функция, которая помогла мне несколько раз:

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, что не всегда легко прочитать).Однако я использовал его не так часто, чтобы гарантировать, что он всегда работает.

20 голосов
/ 03 декабря 2011

Помимо отладчика в Workbench, есть также встроенный в Mathematica отладчик. Вы можете найти его в меню Evaluation. Это не очень хорошо задокументировано и довольно сложно / нетрадиционно заставить его работать. Вот пошаговая инструкция, как его использовать:

Если в меню «Оценка» у вас включен отладчик, ваша панель окна покажет, что это сеанс отладки, и у вас будет несколько палитр отладчика.

enter image description here

Теперь выберите количество строк, которые вы хотите использовать в качестве точек останова, и нажмите на текст «разбить при выделении». Точки останова будут отмечены красным контуром.

enter image description here

и запустите код, нажав Shift-return, и будьте готовы к небольшому разочарованию: он не работает. Похоже, вы не можете определить точки останова на уровне линии. Это должно быть на функциональном уровне. Кроме того, MMA довольно требователен к функциям, которые вы можете использовать. Функция Print, очевидно, не работает и не выполняет назначения. Тем не менее, Integrate в этом примере делает, но вы должны выбрать его голову и обе скобки и сделать , что точкой останова. Если вы сделали это и затем выполнили блок кода, вы получите следующее:

enter image description here

Точка останова подсвечивается зеленым, доступны некоторые дополнительные опции в палитре управления для управления дальнейшим ходом программы, а в окне стека есть выражения. Остальное более или менее похоже на стандартный отладчик. Обратите внимание, что вы можете nest контрольные точки, такие как Cos в Integrate. Для языка, который может иметь глубоко вложенные структуры, это важно.


Другим вариантом будет отладчик Дэвида Бэйли. Он предлагает бесплатный отладчик DebugTrace на своем сайте. Я сам не пробовал, но знаю Дэвида как очень способного эксперта по Mathematica, поэтому я верю, что это должно быть хорошо

17 голосов
/ 03 декабря 2011

Я не знаю, как найти строку в файле, которая, как я полагаю, была прочитана без ошибок.

Однако вы можете использовать Trace и связанные функции, чтобы увидеть, где находится цепочка оценки.возникает ошибка.

Пример:

myFunc1[x_, y_] := x[[y]]
myFunc2[a_List, n_] := Pick[a, a, myFunc1[a, n]]

myFunc2[Range@10, #1]

During evaluation of In[4]:= Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>

С Trace:

myFunc2[Range@10, #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

8 голосов
/ 03 декабря 2011

Вы можете использовать WolframWorkbench и отладчик там:

http://www.wolfram.com/broadcast/screencasts/workbench/debugging/

затем вы можете установить точку останова и пройти по коду.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...