我有一个名为myUsefulFunctions.m的Mathematica文件,其中包含一个名为mySuperUsefulFunction的函数。假设我在笔记本中调用mySuperUsefulFunction并获得以下错误:
Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>
有没有方法可以找到出现错误的代码行在 myUsefulFunctions.m 文件中的位置?
我有一个名为myUsefulFunctions.m的Mathematica文件,其中包含一个名为mySuperUsefulFunction的函数。假设我在笔记本中调用mySuperUsefulFunction并获得以下错误:
Part::pspec: Part specification #1 is neither an integer nor a list of integers. >>
有没有方法可以找到出现错误的代码行在 myUsefulFunctions.m 文件中的位置?
除了其他建议,这里提供了一个函数,在我遇到问题时曾经帮过我的几次:
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
使用 debug
方法来调用 gatherBy[Range[10], a]
,当其中有一些符号 a
时,是另一个例子。
其他建议的方法更加系统化,可能更加普遍推荐,但这种方法易于应用,并且通常会导致更容易理解的结果(例如,与不易阅读的 Trace
输出相比)。然而,我并没有经常使用它以保证它总是有效的。
按下Shift-return运行代码,准备好稍微失望一下:它不起作用。似乎你不能在行级别上定义断点,必须在函数级别上进行。此外,MMA对可以使用的函数非常挑剔。显然Print
函数不起作用,赋值也是如此。然而,在这个例子中,Integrate
函数可以使用,但你必须选择它的头和双括号,并将其作为断点。如果你已经这样做了,然后执行代码块,你会得到这个结果:
另一个选择是David Bailey的调试器。他在他的网站上提供免费的DebugTrace调试器。我自己没有尝试过,但我知道David是一个非常有能力的Mathematica专家,所以我相信它一定很好。
我不知道有没有办法找到在文件中的哪一行出现了错误,因为我假设这个文件是正确读取的。
不过,你可以使用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"我的 " <> Sequence @@ #@# &@"祝贺!"
- Dr. belisarius您可以使用WolframWorkbench和其中的调试器:
http://www.wolfram.com/broadcast/screencasts/workbench/debugging/
然后您可以设置断点并逐步执行代码。
TableForm
中放置Trace
的结果。由于结果通常(总是?)是一个{expr,value}
对的列表,因此TableForm
非常有用。请尝试使用Trace@Cases[{f [{a,b}],f[{a}],g[{a}],f[{a,b,c,d}]},f[x_]:> Length @ x] // TableForm
与Trace@Cases[{f[{a, b}], f[{a}], g[{a}], f[{a, b, c, d}]}, f[x_] -> Length@x] // TableForm
进行比较。 - Reb.CabinTableForm
这样的东西会非常有用。 - Leonid Shifrin