6
$\begingroup$

Sometimes I'll use Print[1] or Print[n] to debug in a long loop, but it's really a boring job to add these sentence by sentence and removing them sentence by sentence. (It's already boring to read this first three lines sentence by sentence so you can imagine how boring it is to add these stuff sentence by sentence. :P) So my question is how to throw this job to a program?

Let's take this small (and stupid) program as an example:

Do[j=i^2;Thread[{{1},{2,3}}],{i,3}]

I would like to add two Print in this program to determin where the problem occurs.

Do[Print@1;j=i^2;Print@2;Thread[{{1},{2,3}}],{i,3}]

In this way I can know that it's the second step that goes wrong.

Any idea how to do this automatically?

$\endgroup$
5
  • $\begingroup$ Workbench has such functionalities. $\endgroup$
    – vapor
    Jun 24, 2016 at 13:51
  • $\begingroup$ @happyfish I think this could be done by only Mathematica. After all, Mathematica operate on expressions and this is a part of it~ $\endgroup$
    – Wjx
    Jun 24, 2016 at 14:17
  • $\begingroup$ It just make things easier, another example: building documentations. $\endgroup$
    – vapor
    Jun 24, 2016 at 14:18
  • 1
    $\begingroup$ @happyfish maybe it will, but I simply don't have one :P $\endgroup$
    – Wjx
    Jun 24, 2016 at 14:20
  • $\begingroup$ ahhhh sorry I didn't take this into account. $\endgroup$
    – vapor
    Jun 24, 2016 at 14:29

6 Answers 6

8
$\begingroup$

In your example, a message is issued at the problem. You want to find out where the message comes from. The debugger is pretty good at this. Just enable the debugger, then enable Break at Messages, then evaluate your code.

enter image description here

  • You get a nice display of the stack, which in itself allows you to localize the problem.

  • Since this was an interactive evaluation, the problem is also highlighted in the notebook

  • If we had a Module there, we could see its local variables in the Stack window, with their values.

  • Evaluation is suspended in you're in a dialog. You can evaluate things and examine the kernel state. Evaluating i gives 1 (even though i is a local variable.

  • You could even put Dynamic[i] in a new windows and watch i change live as you step through the code.

In my opinion, I think the debugger is way underrated. The big problem with it is that it has a breakpoints feature, like traditional debuggers, and people try to use this then get frustrated. There are many reasons why breakpoints just don't fit Mathematica well. Don't use them. The other features work well.

When I do need a breakpoint, I explicitly insert an Assert[False] in the code and enable Break at Asserts.

I know this does not answer your actual question, but I hope you will still find it useful to solve your problem ...

$\endgroup$
7
  • $\begingroup$ but in my real situation I suppose I must use my approch. I'm making a ray tracer, so I need to check the behaviour of one of my light beam at each step. So I'll need to know both the step number and the beam's equation. You know, Print or Echo is always customizable while debugger is not. :P $\endgroup$
    – Wjx
    Jun 24, 2016 at 23:51
  • $\begingroup$ Also, sometimes I will add something like track=1 and Dynamic@track so I can keep informed of which step is going. This is not quite easy to do in debug mode I suppose? $\endgroup$
    – Wjx
    Jun 25, 2016 at 0:11
  • $\begingroup$ So how to get the Stack? $\endgroup$
    – yode
    Jun 25, 2016 at 2:38
  • $\begingroup$ @Wjx Actually I described how to track which steps you're in inside of a Do ... with Dynamic if you like ... $\endgroup$
    – Szabolcs
    Jun 25, 2016 at 15:19
  • $\begingroup$ Well, Seemingly I cannot find anything that cannot be done by debugger in this situation at this point......So what if I simply want to add some sentence with some pattern and thread them into this loop? $\endgroup$
    – Wjx
    Jun 25, 2016 at 15:29
3
$\begingroup$

Actually form C.E.'s comment.

Make a custom function for this

SetAttributes[AddPrint, HoldFirst]
AddPrint[loopBody_] := 
 Module[{st = ToString[Unevaluated[loopBody], InputForm], i = 0}, 
  StringReplace[st, 
   RegularExpression["^|(?<=;)"] :> TemplateApply["Print[``];", ++i]]]

It can help you add Print into your loopBody automatically

loopBody = AddPrint[j = i^2; a + b; Thread[{{1}, {2, 3}}]]

Print[1];j = i^2;Print[2]; a + b;Print[3]; Thread[{{1}, {2, 3}}]

$\endgroup$
2
$\begingroup$

I often use the form

Block[{Print=Identity}, expr]

to suppress Print statements that I have added for debug purposes.

$\endgroup$
1
  • $\begingroup$ That will be great. also With will do the same~ but how can we add these things? $\endgroup$
    – Wjx
    Jun 24, 2016 at 14:39
1
$\begingroup$

My code is executed in two distinct environments: inside WorkBench where I test it, and automatically by kernel. Inside WorkBench $Linked is True, so I set the following:

If[$Linked,
  dbPrint = Print;
];

Then, when I need a debug print out, I used dbPrint which only has a value when being run inside of WorkBench, and does not effect production code. Obviously, other environment specific code can be added to the If statement as needed.

$\endgroup$
2
  • $\begingroup$ I'm confused. I don't have Workbench, but I just evaluated $Linked in a random notebook, and it returned True. The documentation also seems to indicate that "$Linked is True when the Wolfram System is being run with a front end. ". Am I misunderstanding? $\endgroup$
    – MarcoB
    Jun 24, 2016 at 20:55
  • $\begingroup$ @MarcoB right. In this WB acts as the FrontEnd. From what I've seen, it's set to True when a $ParentLink exists. But, don't take that as being canonical info. $\endgroup$
    – rcollyer
    Jun 25, 2016 at 1:27
1
$\begingroup$

Rather than mangling the code by inserting Print/Echo statements into it, you could modify CompoundExpression during run-time. Here is a function that does this:

SetAttributes[injectTiming, HoldFirst]

injectTiming[code_, instance_:Automatic] := Module[{ilevel = 1, target},
    target = Replace[instance, Except[_Integer?Positive] -> 1];
    Internal`InheritedBlock[{Internal`$ContextMarks=False, CompoundExpression},
        Unprotect[CompoundExpression];
        CompoundExpression[a__] /; (ilevel++ == target) := compoundExpression[a];
        code
    ]
]

SetAttributes[compoundExpression,HoldAll];

compoundExpression[a___, b_]  := Module[{last},
    Apply[
        List,
        Function[Null, Print[First @ AbsoluteTiming[#;], ": ", HoldForm[#]], HoldFirst] /@ Hold[a]
    ];
    last = AbsoluteTiming[b];
    Print[First @ last, ": ", HoldForm[b]];

    Last @ last
]

The second argument specifies which CompoundExpression instance should be modified with debug information, with default being the first. For your example:

injectTiming[Do[j=i^2; Thread[{{1},{2,3}}], {i,3}]]

3.*10^-6: j=i^2

Thread::tdlen: Objects of unequal length in {{1},{2,3}} cannot be combined.

0.00228: Thread[{{1},{2,3}}]

Thread::tdlen: Objects of unequal length in {{1},{2,3}} cannot be combined.

Thread::tdlen: Objects of unequal length in {{1},{2,3}} cannot be combined.

General::stop: Further output of Thread::tdlen will be suppressed during this calculation.

$\endgroup$
0
$\begingroup$

Yesterday I checked my account's question list again and find this question lying inside. After a year wrestling mostly with Hold stuffs, I think I finally found a, well, not that elegant solution. Any suggestions or new answers are surely welcomed!!!

It seems that this question is not that bad, actually I may say that It's quite hard for one to solve this problem using all sorts of evaluation manipulation techniques. and it has real applications sometimes.


As usual, my code first:

SetAttributes[adddebug, HoldFirst]
adddebug[orig_, func_: Echo, level_: {2}] :=
 Module[{$count = 1, tempf},
  Replace[
    Replace[Hold[orig], HoldPattern[CompoundExpression[comp__]] :> 
      Block[{}, 
       tempf @@ 
         Riffle[List @@ (Hold /@ Hold[comp]), 
          With[{$func = func, q = #}, Hold@$func@q] & /@ 
           Range[$count, ($count = $count + Length@Hold[comp] + 1) - 
             1], {2, -1, 2}] /; True],level], 
    tempf[seg__] :> 
     Block[{}, 
      tempf[Hold[CompoundExpression]] @@ (tempf /@ {seg}) /; True],level] /. tempf[Hold[seg_]] :> seg
  ]

adddebug[Do[j = i^2; Thread[{{1}, {2, 3}}], {i, 3}]]

The result is satisfying

Hold[Do[j = i^2; Echo[1]; Thread[{{1}, {2, 3}}]; Echo[2], {i, 3}]]


Some additional notes:

Why I say this question is not that trivial is because I should protect CompoundExpression and its content from evaluating while still need to operate on CompoundExpression's structure and use in place evaluation techniques extensively.

So I install protection in the following ways:

  1. When pattern-matching, Use HoldPattern

  2. Use unique symbol tempf as a medium to mark Where CompoundExpression previoulsly existed. Use tempf[Hold[CompoundExpression]].

  3. make every element of CompoundExpression Hold at all times, and mark them using tempf[Hold[expr]]

  4. I'm quite satisfied by the last ReplaceAll's design, as previously I’ve already make where should be CompoundExpression tempf[Hold[CompoundExpression]] and where should be CompoundExpression's element tempf[Hold[element]], Now I can safely use ReplaceAll once to make those stuffs back to their original form!


I still am looking foward to a better solution, so if anyone has any idea, post something, Thanks!

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.