开发者

How to debug when writing small or big codes using Mathematica? workbench? mma debugger? or something else?

At mathkb.com, I found a interesting post "Another review of Mathematica's debugger" (by berniethejet) talking about debugging in wolfram workbench.

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

I think this is a good question worth discussing and I would like hear some experiences of using workbench, even though I've never touched workbench.

  1. Is workbench a real debugger but a watcher? what's开发者_开发百科 its advantage over mathematica?
  2. How do you debug when you writting big or small codes? mabye workbench is for debugging small codes and mma debugger for large codes ?
  3. Any suggestion on debugging for both light and heavy mathemata users?


Debuggers are generally more useful when you program in the stateful style (variables, assignments, etc) - at least that has been my experience. For idiomatic Mathematica programming (functional/rule-based), some versions of Print statements are at least as effective. You may look at this post for some variants of debug print utility. I will throw in my version taken from this Mathgroup post.

SetAttributes[ShowIt, HoldAll];
ShowIt[code_] :=
  Module[{y},
    Print[ToString[Unevaluated[code]], " = ", y = code];
    y]; 

The idea is that you can insert such function call into a "pipe" of function calls - it prints the value but then passes it to the next (surrounding) function. As a simple example:

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}

This should work fine in most cases (except possibly those where the surrounding function holds its arguments and acts on them non-trivially). One of the reasons that this approach is very effective in Mathematica is that functional programming leads to programs in which (almost) every piece makes sense by itself - since the result of one function is typically passed directly to the enclosing function.

That said, you can certainly use the debugger, both within interactive session and in the WorkBench, using "Debug As Mathematica" regime. While I use WorkBench a lot myself, I never found this necessary, but YMMV.

Another great facility that helps a lot is a built-in Trace command. I recommend reading the documentation on it - it has a number of advanced options and can be customized to help a great deal. I will give one simple but non-trivial example: tracing the execution of the mergesort algorithm, with the following (simplistic) implementation:

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]]]]

We will take a very small input list, just to reduce the length of the output:

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

Out[41]= {0, 6, 9, 8, 8}

You could just use Trace[mergesort[testlst]];, but the output is not very easy to read, since it contains all the steps. By using

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}]}}}}

You get a very clear picture of recursive function calls. You can go deeper and trace the dynamics of merge function. For that, you have to process the result of Trace (which is also a Mathematica expression!):

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}]}

This last example illustrates that, even when it is hard to configure Trace directly to filter out unwanted execution steps, one can simply post-process the results of Trace using standard means that Mathematica provides for expression destructuring (such as Cases).

Let me also mention that an expert Mathematica user and consultant David Bailey wrote a package DebugTrace, which is supposed to be an alternative debugger. I did not have a chance yet to try it, but I am sure it is worth the try.

Finally, while this is not directly related to debugging, WorkBench has an integrated unit testing framework MUnit, which I found very useful. It is similar in spirit to well-known unit-testing frameworks in other languages, such as JUnit for Java. For large-scale development, this can be a real help.

Regarding the uses of WorkBench, I'd say that it really pays off to use it for anything except the smallest projects (or even for them). It is based on Eclipse, and you get the same nice things, such as the editor with code highlighting, "go to function definition" capability, navigation, search, CVS/SVN integration, etc. At the same time, you don't lose almost anything in terms of interactivity - you can still develop the new functionality in the interactive Mathematica session linked to WorkBench when working in the "Run as Mathematica" regime. For larger projects involving many packages, I just don't see any reason not to use it.


Using the debugger in Wolfram Workbench makes debugging simple and effective. The reason I started to use Workbench was the debugger. The Workbench also supports MUnit the Mathematica variant of JUnit. - "Test first, then code."

The debugger in Workbench supports everything that I expected from a debugger. I have used the Java debuggers in Eclipse and NetBeans.

At least give the debugger a try, so that you can compare. There is a tutorial on the Workbench Docs site.


Here are some variations of ShowIt described by Leonid. Defining them in the System context allows to use them easily in packages.

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];

Example:

ShowIt[{x=2,x=3}]
PrintIt[{x=2,x=3}]

The output of these functions can be easily reused in the frontend by changing its style to "Input".


I've had limited success with the debugger, mostly because I never took the time to properly learn it. I do use one technique often though. Instead of resorting to print statements, I make expressions under my manipulate (or whatever), of the form Dynamic[var]. Your can easliy monntor any file global variable in real time this way without generating huge output. To see manipulate variables, use LocalizeVariables->False and do the same thing. Outside of the manipulate context, the variables are visible, but not dynamic; their monitoring is thus the same.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜