如何找到Mathematica笔记本中发生错误的行?

时间:2011-12-02 20:23:17

标签: debugging wolfram-mathematica

我有一个名为myUsefulFunctions.m的Mathematica文件,其中包含一个名为mySuperUsefulFunction的函数。假设我在笔记本中调用mySuperUsefulFunction并得到以下错误:

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

有没有办法在myUsefulFunctions.m中找到发生此错误的行?

4 个答案:

答案 0 :(得分:21)

轻量级调试功能

除了其他建议之外,这里有一个帮我几次的功能:

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的输出相比,这并非总是如此易于阅读)。然而,我并没有经常使用它来保证它始终有效。

答案 1 :(得分:20)

除了Workbench中的调试器之外,Mathematica中还内置了一个调试器。您可以在“评估”菜单中找到它。它没有很好的记录,而且很难/非传统地使它发挥作用。以下是如何使用它的逐步说明:

假设您已在评估菜单中打开调试器,您的窗口栏将指示它是一个调试会话,您将有一些调试器调色板。

enter image description here

现在选择要作为断点的多行,然后单击“选择中断”文本。断点将用红色轮廓标记。

enter image description here

并按Shift-return运行代码并准备稍微失望:它不起作用。您似乎无法在线路级别定义断点。它必须处于功能级别。此外,MMA对您可以使用的功能相当挑剔。 Print功能显然不起作用。但是,此示例中的Integrate确实如此,但您必须选择其头部和两个括号,并使 成为断点。如果你已经这样做了,那么你执行代码块就可以得到这个:

enter image description here

断点突出显示为绿色,控制面板中的一些其他选项可用于控制进一步的程序流,并且堆栈窗口中有表达式。其余部分或多或少类似于标准调试器。请注意,您可以像集成中的Cos一样嵌套断点。对于可以具有深层嵌套结构的语言,这是必不可少的。


另一个选择是David Bailey的调试器。他在他的网站上提供免费的调试器DebugTrace。我自己没有尝试过,但我知道大卫是一位非常有能力的Mathematica专家,所以我相信它一定很好。

答案 2 :(得分:17)

我不知道在文件中找到该行的方法,我认为该行没有错误。

但是,您可以使用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

答案 3 :(得分:8)

您可以在那里使用WolframWorkbench和调试器:

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

然后你可以设置一个断点并逐步完成代码。