使用Mathematica编写小代码或大代码时如何调试?工作台? mma调试器?或者是其他东西?

时间:2011-05-29 10:49:09

标签: wolfram-mathematica

在mathkb.com上,我发现了一篇有趣的帖子 “Mathematica调试器的另一篇评论”  (由berniethejet)谈论在wolfram工作台中的调试。

http://www.mathkb.com/Uwe/Threads/List.aspx/mathematica/20986

我认为这是一个值得讨论的好问题,我希望听到使用工作台的一些经验,即使我从未触及过工作台。

  1. 工作台是一个真正的调试器但是 观察者?它的优势是什么? 数学?
  2. 你如何调试     当你写大或小     代码? mabye workbench用于调试小代码和mma调试器用于大型代码吗?
  3. 有关轻型和重型数学用户调试的任何建议吗?

4 个答案:

答案 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并执行相同的操作。在操纵上下文之外,变量是可见的,但不是动态的;因此他们的监测是一样的。