在mathkb.com上,我发现了一篇有趣的帖子 “Mathematica调试器的另一篇评论” (由berniethejet)谈论在wolfram工作台中的调试。
http://www.mathkb.com/Uwe/Threads/List.aspx/mathematica/20986
我认为这是一个值得讨论的好问题,我希望听到使用工作台的一些经验,即使我从未触及过工作台。
答案 0 :(得分:20)
当您使用有状态样式(变量,赋值等)进行编程时,调试器通常更有用 - 至少这是我的经验。对于惯用的Mathematica编程(基于功能/规则),某些版本的Print
语句至少同样有效。对于调试打印实用程序的某些变体,您可以查看this帖子。我将从this Mathgroup帖子中获取我的版本。
SetAttributes[ShowIt, HoldAll];
ShowIt[code_] :=
Module[{y},
Print[ToString[Unevaluated[code]], " = ", y = code];
y];
这个想法是你可以将这样的函数调用插入到函数调用的“管道”中 - 它打印该值,然后将其传递给下一个(周围)函数。举个简单的例子:
In[29]:= Map[#^2&,ShowIt@Select[Range[10],EvenQ]]
During evaluation of In[29]:= Select[Range[10], EvenQ] = {2,4,6,8,10}
Out[29]= {4,16,36,64,100}
在大多数情况下这应该可以正常工作(除了那些周围函数保存其参数并且非常简单地对它们起作用的情况)。这种方法在Mathematica中非常有效的原因之一是函数式编程导致程序中(几乎)每个部分本身都有意义 - 因为一个函数的结果通常直接传递给封闭函数。
也就是说,您当然可以使用“Debug As Mathematica”制度在交互式会话和WorkBench中使用调试器。虽然我自己经常使用WorkBench,但我从未发现这是必要的,但是YMMV。
另一个很有帮助的设施是内置的Trace命令。我建议阅读它上面的文档 - 它有许多高级选项,可以自定义以提供很多帮助。我将给出一个简单但非平凡的例子:跟踪mergesort算法的执行,使用以下(简单)实现:
Clear[merge];
merge[{}, {x__}] := {x};
merge[{x__}, {}] := {x}
merge[{x__?NumericQ}, {y__?NumericQ}] /; First[{x}] <= First[{y}] :=
Flatten[{First[{x}], merge[Rest[{x}], {y}]}];
merge[{x__?NumericQ}, {y__?NumericQ}] := merge[{y}, {x}];
Clear[mergesort];
mergesort[x : {} | {_}] := x;
mergesort[x : {__?NumericQ}] :=
With[{splitlen = IntegerPart[Length[x]/2]},
merge[mergesort[Take[x, splitlen]], mergesort[Drop[x, splitlen]]]]
我们将采用一个非常小的输入列表,只是为了减少输出的长度:
In[41]:= testlst = RandomInteger[10, 5]
Out[41]= {0, 6, 9, 8, 8}
您可以使用Trace[mergesort[testlst]];
,但输出不是很容易阅读,因为它包含所有步骤。使用
In[42]:= Trace[mergesort[testlst],_mergesort]
Out[42]= {mergesort[{0,6,9,8,8}],{mergesort[{0,6}],{mergesort[{0}]},
{mergesort[{6}]}},{mergesort[{9,8,8}],{mergesort[{9}]},{mergesort[{8,8}],
{mergesort[{8}]},{mergesort[{8}]}}}}
您可以非常清楚地了解递归函数调用。您可以更深入地追踪merge
函数的动态。为此,你必须处理Trace
的结果(这也是一个Mathematica表达式!):
In[43]:=
Cases[Trace[mergesort[testlst],_merge],merge[x__List]/;FreeQ[{x},mergesort]:>
HoldForm[merge[x]],Infinity]
Out[43]= {merge[{0},{6}],merge[{},{6}],merge[{8},{8}],merge[{},{8}],
merge[{9},{8,8}],merge[{8,8},{9}],merge[{8},{9}],merge[{},{9}],merge[{0,6},
{8,8,9}],merge[{6},{8,8,9}],merge[{},{8,8,9}]}
最后一个示例说明,即使很难直接配置Trace
来过滤掉不需要的执行步骤,也可以使用Mathematica提供的标准方法简单地对Trace
的结果进行后处理。表达式解构(例如Cases
)。
我还要提到Mathematica专家和顾问David Bailey写了一个包DebugTrace,它应该是一个替代调试器。我没有机会尝试,但我相信值得一试。
最后,虽然这与调试没有直接关系,但WorkBench有一个集成的单元测试框架MUnit,我发现它非常有用。它在精神上类似于其他语言中众所周知的单元测试框架,例如JUnit for Java。对于大规模开发,这可能是一个真正的帮助。
关于WorkBench的用途,我会说除了最小的项目(甚至是他们)之外,将它用于任何事情都是值得的。它基于Eclipse,你得到了同样好的东西,比如带代码突出显示的编辑器,“转到功能定义”功能,导航,搜索,CVS / SVN集成等。同时,你没有在交互性方面几乎失去了任何东西 - 在“Run as Mathematica”制度下工作时,您仍然可以在与WorkBench链接的交互式Mathematica会话中开发新功能。对于涉及许多包的大型项目,我认为没有任何理由不使用它。
答案 1 :(得分:4)
在Wolfram Workbench中使用调试器使调试变得简单有效。我开始使用Workbench的原因是调试器。 Workbench还支持MUnit JUnit的Mathematica变体。 - “首先测试,然后测试代码。”
Workbench中的调试器支持我从调试器中获得的所有内容。我在Eclipse和NetBeans中使用过Java调试器。
至少尝试调试器,以便进行比较。 Workbench Docs网站上有一个教程。
答案 2 :(得分:3)
以下是Leonid描述的ShowIt的一些变体。在System上下文中定义它们允许在包中轻松使用它们。
SetAttributes[System`ShowIt, HoldAll];
System`ShowIt[code__] := System`ShowIt[{code}];
System`ShowIt[code_] :=
With[{y = code},
Print[Defer[code = y]];
y
];
SetAttributes[System`PrintIt, {HoldAll,Listable}];
System`PrintIt[expr__]:=System`PrintIt[{expr}];
System`PrintIt[expr_] := System`ShowIt[expr];
示例:
ShowIt[{x=2,x=3}]
PrintIt[{x=2,x=3}]
通过将其样式更改为“输入”,可以在前端轻松重用这些函数的输出。
答案 3 :(得分:2)
我在调试器方面的成功有限,主要是因为我从未花时间正确地学习它。我经常使用一种技术。我没有使用print语句,而是在Dynamic [var]形式的操作(或其他)下创建表达式。您可以通过这种方式实时查看任何文件全局变量,而不会产生巨大的输出。要查看操作变量,请使用LocalizeVariables-&gt; False并执行相同的操作。在操纵上下文之外,变量是可见的,但不是动态的;因此他们的监测是一样的。