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

Fre*_*ple 12 wolfram-mathematica

在mathkb.com,我发现了一篇有趣的文章"Mathematica调试器的另一篇评论" (berniethejet),讨论了在wolfram工作台中的调试.

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

我认为这是一个值得讨论的好问题,我想听一些使用工作台的经验,尽管我从未接触过工作台.

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

Leo*_*rin 20

当您使用有状态样式(变量,赋值等)进行编程时,调试器通常更有用 - 至少这是我的经验.对于惯用的Mathematica编程(基于功能/规则),某些Print语句版本至少同样有效.您可以查看帖子,了解调试打印实用程序的一些变体.我会扔在我的取自版本这个 Mathgroup职位.

SetAttributes[ShowIt, HoldAll];
ShowIt[code_] :=
  Module[{y},
    Print[ToString[Unevaluated[code]], " = ", y = code];
    y]; 
Run Code Online (Sandbox Code Playgroud)

这个想法是你可以将这样的函数调用插入到函数调用的"管道"中 - 它打印该值,然后将其传递给下一个(周围)函数.举个简单的例子:

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}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这应该可以正常工作(除了那些周围函数保存其参数并且非常简单地对它们起作用的情况).这种方法在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]]]]
Run Code Online (Sandbox Code Playgroud)

我们将采用一个非常小的输入列表,只是为了减少输出的长度:

In[41]:= testlst = RandomInteger[10, 5]

Out[41]= {0, 6, 9, 8, 8}
Run Code Online (Sandbox Code Playgroud)

您可以使用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}]}}}}
Run Code Online (Sandbox Code Playgroud)

您可以非常清楚地了解递归函数调用.您可以更深入地追踪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}]}
Run Code Online (Sandbox Code Playgroud)

最后一个例子说明,即使很难Trace直接配置以过滤掉不需要的执行步骤,也可以简单地Trace使用Mathematica为表达式解构提供的标准方法(例如Cases)后处理结果.

我还要提一下,专家Mathematica用户和顾问David Bailey编写了一个包DebugTrace,它应该是一个替代调试器.我没有机会尝试,但我相信值得一试.

最后,虽然这与调试没有直接关系,但WorkBench有一个集成的单元测试框架MUnit,我发现它非常有用.它在精神上类似于其他语言中众所周知的单元测试框架,例如JUnit for Java.对于大规模开发,这可能是一个真正的帮助.

关于WorkBench的用途,我会说除了最小的项目(甚至是最小的项目)之外,将它用于任何事情都是值得的.它基于Eclipse,你得到了同样好的东西,比如带代码突出显示的编辑器,"转到功能定义"功能,导航,搜索,CVS/SVN集成等.同时,你没有在交互性方面几乎失去了任何东西 - 在"Run as Mathematica"制度下工作时,您仍然可以在与WorkBench链接的交互式Mathematica会话中开发新功能.对于涉及许多包的大型项目,我认为没有任何理由不使用它.