Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

WReach has presented here a nice way to represent the Mathematica's evaluation sequence using OpenerView. It is much more clear way to go than using the standard Trace or TracePrint commands. But it could be improved further.

I need straightforward way to represent the real sequence of (sub)evaluations inside Mathematica's main loop for beginners. In particular, it should be obvious when new evaluation subsequence begins and from which expression (it is better to have each subsequence exactly in one Opener). The evaluation (sub)sequence should be identified as easily as possible with the standard evaluation sequence. I mean that the reader should be able to map real evaluation step to one described in the Documentation for the standard evaluation sequence.

Is it possible?

share|improve this question
add comment

migrated from stackoverflow.com Jul 28 '13 at 6:31

This question came from our site for professional and enthusiast programmers.

3 Answers

up vote 45 down vote accepted

The cited OpenerView solution used Trace / TraceOriginal to generate its content. This allowed the definition of show in that response to be defined succinctly, but had the disadvantage of discarding some of the trace information. TraceScan provides more information since it calls a user-specified function at the start and end of every evaluation.

Two functions are defined below that try to format the TraceScan information in (somewhat) readable form.

traceView2 shows each expression as it is evaluated, along with the subevaluations ("steps") that lead to the result of that evaluation. "Drill-down" is provided by OpenerView. The function generates output that looks like this:

traceView2[(a + 1) + 2]

traceView2 screenshot

As one drills deeper into the view, it rapidly crawls off the right-hand side of the page. traceView4 provides an alternative view that does not exhibit the crawling behaviour at the expense of showing much less context for any given evaluation:

traceView4 screenshot

Choose your poison ;)

The definitions of the functions follow...

traceView2

ClearAll@traceView2
traceView2[expr_] :=
  Module[{steps = {}, stack = {}, pre, post, show, dynamic},
    pre[e_] := (stack = {steps, stack}; steps = {})
  ; post[e_, r_] :=
      ( steps = First@stack ~Join~ {show[e, HoldForm[r], steps]}
      ; stack = stack[[2]]
      )
  ; SetAttributes[post, HoldAllComplete]
  ; show[e_, r_, steps_] :=
      Grid[
        steps /. {
          {} -> {{"Expr  ", Row[{e, " ", Style["inert", {Italic, Small}]}]}}
        , _ -> { {"Expr  ", e}
               , {"Steps", steps /.
                   { {} -> Style["no definitions apply", Italic]
                   , _ :> OpenerView[{Length@steps, dynamic@Column[steps]}]}
                 }
               , {"Result", r}
               }
        }
      , Alignment -> Left
      , Frame -> All
      , Background -> {{LightCyan}, None}
      ]
  ; TraceScan[pre, expr, ___, post]
  ; Deploy @ Pane[steps[[1]] /. dynamic -> Dynamic, ImageSize -> 10000]
  ]
SetAttributes[traceView2, {HoldAllComplete}]

traceView4

ClearAll@traceView4
traceView4[expr_] :=
  Module[{steps = {}, stack = {}, pre, post},
    pre[e_] := (stack = {steps, stack}; steps = {})
  ; post[e_, r_] :=
      ( steps = First@stack ~Join~ {{e, steps, HoldForm[r]}}
      ; stack = stack[[2]]
      )
  ; SetAttributes[post, HoldAllComplete]
  ; TraceScan[pre, expr, ___, post]
  ; DynamicModule[{focus, show, substep, enter, exit}
    , focus = steps
    ; substep[{e_, {}, _}, _] := {Null, e, Style["inert", {Italic, Small}]}
    ; substep[{e_, _, r_}, p_] :=
        { Button[Style["show", Small], enter[p]]
        , e
        , Style[Row[{"-> ", r}], Small]
        }
    ; enter[{p_}] := PrependTo[focus, focus[[1, 2, p]]]
    ; exit[] := focus = Drop[focus, 1]
    ; show[{e_, s_, r_}] :=
       Column[
         { Grid[
             { {"Expression", Column@Reverse@focus[[All, 1]]}
             , { Column[
                   { "Steps"
                   , focus /.
                       { {_} :> Sequence[]
                       , _ :> Button["Back", exit[], ImageSize -> Automatic]
                       }
                   }
                 ]
               , Grid[MapIndexed[substep, s], Alignment -> Left]
               }
             , {"Result", Column@focus[[All, 3]]}
             }
           , Alignment -> Left, Frame -> All, Background -> {{LightCyan}}
           ]
         }
       ]
    ; Dynamic @ show @ focus[[1]]
    ]
  ]
SetAttributes[traceView4, {HoldAllComplete}]
share|improve this answer
1  
Very nice! Now the sequence of evaluations is really visually clear! One thing that Trace* commands totally loose is applying the Orderless attribute. I was amazed detecting that Plus[a, 1] in really is evaluated again in the form Plus[1, a] after applying the Orderless attribute! It seems to contradict the standard evaluation sequence description. –  Alexey Popkov Apr 3 '11 at 7:39
    
Nifty. Now code something similar for XML... (if you have nothing better to do) :) +1. –  telefunkenvf14 Apr 3 '11 at 10:27
    
Could you please explain why you only apply Dynamic at the end in traceView2, by substituting every dynamic with Dynamic? For performance reasons? –  István Zachar Mar 9 '12 at 1:47
    
@IstvánZachar The dynamic substitution is unnecessary -- it is a leftover piece of code from a previous version of the function. It could even be considered harmful as it is preferable to defer the creation of the inner OpenerViews. I'd fix it, but I notice that these functions now perform very poorly for complex traces in versions of Mathematica released since I wrote this response. It seems that some relevant performance characteristic changed in a later microrevision of V7 (possibly V7.0.1). These functions need to be revisited (but I cannot do that right at the moment). –  WReach Mar 10 '12 at 18:36
4  
Why don't you put this code on GitHub? Right now people may be copying this code from here, and modifying it for their personal use. But the improvements never make it back here for the whole community's benefit. GitHub would encourage people to either contribute the changes back, or at least would make it easier to track down modified version. Why GitHub and not another site? Because GitHub allows people to edit the source in a browser without even needing to install git. This can actually work with short functions like this. –  Szabolcs Mar 5 '13 at 18:29
add comment

Just an update on WReach's extremely useful traceView function: more compact view, larger buttons for opening/collapsing hierarchy, saves button-states as well.

ClearAll[traceViewCompact];
SetAttributes[traceViewCompact, {HoldAllComplete}];
traceViewCompact[expr_] := 
  Module[{steps = {}, stack = {}, pre, post, show, default = False},
   pre[e_] := (stack = {steps, stack}; steps = {});
   post[e_, 
     r_] := (steps = First@stack~Join~{show[e, HoldForm@r, steps]}; 
     stack = stack[[2]]);
   SetAttributes[post, HoldAllComplete]; 
   show[e_, r_, steps_] := Module[{open = False},
     Grid[
      steps /. {{} -> {{"Expr  ", 
           Item[e, Background -> GrayLevel@.8]}}, _ -> {{"Expr  ", 
           e}, {Toggler[
            Dynamic@
             open, {True -> 
              Button["Steps", Appearance -> {"DialogBox", "Pressed"}],
              False -> Button@"Steps"}], 
           steps /. {{} -> Style["no definitions apply", Italic], _ :>
               Dynamic@
               If[open, Column@steps, 
                Grid@{{Length@steps, "steps"}}]}}, {"Result", r}}}, 
      Alignment -> {Left, Center}, Frame -> All, 
      Spacings -> Automatic, Background -> {{Hue[.65, .1, 1]}, None}]
     ];
   TraceScan[pre, expr, ___, post];
   Deploy@Column@{
      Opener@Dynamic@default,
      Dynamic@Pane[First@steps, ImageSize -> 10000]
      }];


traceViewCompact[(a + 1) + 2]

Mathematica graphics

share|improve this answer
add comment

Here's my approach, also based on WReach's OpenerView technique. Its layout is much more compact, though less explicit, than his traceView2, and as far as I can tell, the only information sacrificed is the display of the number of steps hidden inside each OpenerView. Expressions that are unchanged by evaluation are indicated by a disabled OpenerView, though the screenshot that I made doesn't show a difference between a disabled and enabled OpenerView.

SetAttributes[TraceView, HoldFirst]

TraceView[e_, s___, opts : OptionsPattern[Trace]] := 
 Module[{show2},
  show2[{expr_, steps__}] := 
  OpenerView[{expr, Column[show2 /@ {steps}]}]; 
  show2[{HoldForm[x_]}] := Row[{Opener[True, Enabled -> False], HoldForm[x]}]; 
  show2[HoldForm[x_]] := HoldForm[x];
  show2[Trace[Unevaluated[e], s, opts, TraceOriginal -> True]]
 ]

TraceView output

share|improve this answer
    
It is interesting to note that outputs of TraceScan and Trace with TraceOriginal -> True slightly differs. In the second case evaluation chain for (a+1) is shown with additional step "a+1" before step "1+a". I do not understand what this additional step does represent. Have anybody any ideas? –  Alexey Popkov Apr 6 '11 at 5:23
    
I have answered this question in separate thread. –  Alexey Popkov Mar 20 '12 at 8:07
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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