Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?
like image 590
FreshApple Avatar asked May 29 '11 10:05

FreshApple


1 Answers

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.

like image 57
Leonid Shifrin Avatar answered Jan 17 '23 21:01

Leonid Shifrin