4
$\begingroup$

I would like a version of TraceScan that gives me the ability to see what the trace pattern evaluated to, but prints output in the order that TracePrint does. One idea is to use TraceScan as follows:

f[0]=0;
f[1]=1;
f[n_]:=f[n-1]+f[n-2]

TraceScan[Identity, f[3], _f, Print[#1 -> #2]&]

(*
f[1]->1

f[2-1]->1

f[0]->0

f[2-2]->0

f[2]->1

f[3-1]->1

f[1]->1

f[3-2]->1

f[3]->2

2
*)

However, the order above is completely different than one sees when using TracePrint:

TracePrint[f[3], _f]

(*
  f[3]

   f[3-1]

   f[2]

    f[2-1]

    f[1]

    f[2-2]

    f[0]

   f[3-2]

   f[1]

 2
*)

Is there any way to combine the order one sees with TracePrint, with the ability to see what each pattern evaluated to?

$\endgroup$
1
  • $\begingroup$ Related: (29339) $\endgroup$
    – Mr.Wizard
    Sep 2, 2017 at 12:49

2 Answers 2

5
$\begingroup$

Here is a function that does this:

BeginPackage["TracePrintEvaluate`"];

TracePrintEvaluate::usage = Uncompress@"1:eJy9UEFuAjEMhJ+4OSMKvdByBLUSNwTc\
YA9hMWykbIxipwtCfJd3NFmEiijlyGVsaWzPjF+WNJmemo3Gov/VG3Zr/KjxfULVgHbzg5p5ne\
PYGyef39oGLahaoOYRLiNT2VtMncLd1id2NlJZLK3Y/5Jr8uWFPCY6U8esFuvANt1n0A6Me6Ug\
ILTZWPQQlyBdRWZDjqEqTF5AqSXiX9P3xc4abVh0um/PCMr/pTQut2GFMae1gGeRq1SBEcRrx8\
l8zYAPNo0zU26ioRVURgqQAoH35ZLswx/w7QN+AGO4nlU=";

Begin["`Private`"];

Options[TracePrintEvaluate]=Join[
    FilterRules[Options[TracePrint], Except[TraceInternal|TraceAction]],
    {
    "TraceIn" -> None, (* Side effect function applied to input *)
    "TraceOut" -> None, (* Side effect function applied to output *)
    "Timing" -> False, (* Whether to show evaluation time *)
    "Toggler" -> 1, (* Which toggler state to show initially *)
    "StripDynamics" -> True, (* Whether to clear dynamic variables *)
    TraceInternal -> True
    }
];

SetAttributes[TracePrintEvaluate, HoldAll];

TracePrintEvaluate[expr_, form_, opts:OptionsPattern[]] := Module[
    {
    res, index=0, values, timing, start, outAction,
    toggle = Replace[OptionValue["Toggler"], Except[_Integer]->1],
    showtime = TrueQ@OptionValue["Timing"],
    in = OptionValue["TraceIn"],
    out = OptionValue["TraceOut"],
    purge = TrueQ@OptionValue["StripDynamics"]
    },

    (* What to show if a step hasn't finished evaluating *)
    values /: MakeBoxes[_values, fmt_] := "...";
    timing /: MakeBoxes[_timing, fmt_] := "...";
    outAction /: MakeBoxes[_outAction, fmt_] := "...";

    If[purge,
        (* Set CellTags for the evaluation cell so that printed 
         * cells have the tag *)
        SetOptions[EvaluationCell[], CellTags->"TracePrintEvaluate"],

        (* If not burning module variable values into the trace cells, remove
         * Temporary attribute so that trace cells can still
         * use the values when toggling *)
        ClearAttributes[{values, timing, start, outAction}, Temporary]
    ];

    Block[{stack = {}},
        With[
            {
            tprint = With[{v = push @ index++},
                Print @ TPESelector[
                    {
                    indented[TraceLevel[]-1, Defer@@#], (* input *)
                    visibleForm@values[v], (* output *)
                    If[showtime, timing[v], Nothing],
                    If[in=!=None, visibleForm@in[#], Nothing],
                    If[out=!=None, visibleForm@outAction[v], Nothing]
                    },
                    toggle
                ];
                start[v] = AbsoluteTime[]
            ]&,
            tset = Function[Null, 
                With[{v=pop[]},
                    timing[v] = AbsoluteTime[]-start[v];
                    values[v] = #2;
                    (* Perform additional TraceAction if specified *)
                    If[out=!=None, outAction[v] = out[#1,#2]]
                ],
                SequenceHold
            ],
            tsopts = FilterRules[{opts}, Options@TraceScan]
            },

            (* Main trace code *)               
            res = TraceScan[
                tprint,
                expr,
                form,
                tset,
                tsopts,
                TraceInternal->OptionValue[TraceInternal]
            ];

            If[purge,
                (* Clear evaluation cell tag *)
                SetOptions[EvaluationCell[], CellTags->{}];

                (* Select all TracePrintEvaluate cells, and clear tags *)
                NotebookFind[
                    EvaluationNotebook[],
                    "TracePrintEvaluate",
                    All,
                    CellTags,
                    AutoScroll->False
                ];
                FrontEndExecute @ FrontEnd`SelectionRemoveCellTags[
                    EvaluationNotebook[],
                    "TracePrintEvaluate"
                ];

                (* Overwrite old TracePrint cells with new version where values
                 * and timing have values *)
                NotebookWrite[
                    EvaluationNotebook[],
                    NotebookRead[EvaluationNotebook[]]
                ];

                (* Clear module variables now that their values have been 
                 * burned into the TracePrint cells *)
                Clear[timing, values, start, outAction]
            ];

            res
        ]
    ]
];

(* A lightweight stack *)   
push[x_] := Last[stack = {stack, x}]
pop[] := (stack=#; #2)& @@ stack

TPESelector /: MakeBoxes[TPESelector[{in_,out__}, init_Integer:1], fmt_] :=
TemplateBox[
    {MakeBoxes[in, fmt], out},
    "TPESelector",
    DisplayFunction->TPEDisplayFunction[Length[{out}], init],
    InterpretationFunction->(#&)
]

(* A minor bug is worked around by blocking TemplateArgBox *)
TPEDisplayFunction[len_, n_] := With[
    {
        display = Join[
            {1->#1},
            Thread @Rule[
                Range[2, len+1],
                Composition[DynamicBox,ToBoxes,Slot]/@Range[2,len+1]
            ]
        ]
    },
    Function @ DynamicModuleBox[{i=n},
        TagBox[
            TogglerBox[Dynamic[i], display, ImageSize->Automatic],
            EventHandlerTag[{
                {"MouseClicked",2} :> Block[{Global`TemplateArgBox=#&},
                    If[i==1,
                        CopyToClipboard@Cell[StripBoxes@BoxData@#1, "Input"],
                        CopyToClipboard@Cell[
                            StripBoxes@BoxData@ToBoxes[
                                {TemplateSlotSequence[2]}[[i-1]]
                            ],
                            "Input"
                        ]
                    ]
                ]
            }]
        ]
    ]
];

(* Use an explicit RowBox instead of Row so that StripBoxes will 
 * strip the indenting *)
indented /: MakeBoxes[indented[n_, e_], fmt_] := RowBox[{
    ToBoxes[Indent[n]],
    MakeBoxes[e,fmt]
}]

(* The output should show Sequence objects, and I replace "" with "\"\""
 * so that empty strings are visible and selectable *)
SetAttributes[visibleForm,SequenceHold]
visibleForm /: MakeBoxes[visibleForm[""], fmt_] := StyleBox[
    "\"\"",
    ShowStringCharacters->True
]
visibleForm /: MakeBoxes[visibleForm[u_], fmt_] := MakeBoxes[u,fmt]

End[];

EndPackage[];

For your example:

TracePrintEvaluate[f[3], _f]

(*
 f[3]

  f[3-1]

  f[2]

   f[2-1]

   f[1]

   f[2-2]

   f[0]

  f[3-2]

  f[1]

2
*)

You see that the output looks identical to TracePrint. However, if you click on any of the traced cells, you will see what that pattern evaluated to. Another nice feature is that right clicking on a cell will copy the output in a way that can be pasted and evaluated. The TracePrint/TraceScan output typically has a HoldForm wrapped around the trace pattern that "mysteriously" prevents evaluation.

Finally, you can use the "Timing" option to include the timing of the evaluation of the pattern in the toggler, as well as "TraceIn" and "TraceOut" options that allow you to add additional information to the toggler. The "TraceIn" option takes the trace pattern as an argument, and the "TraceOut" option takes the trace pattern and what it evaluated to as arguments.

If you like to use TracePrint, I think you will find that TracePrintEvaluate adds some nice features.

$\endgroup$
1
$\begingroup$

I'm sure this doesn't have all the features of your extensive package code but it seems like it gives the same result for this simple example.

SetAttributes[myTracePrint, HoldAll]

myTracePrint[expr_, pat_, opts : OptionsPattern[TraceScan]] :=
  DynamicModule[{res},
    TraceScan[
      Print[Spacer[5 TraceLevel[]], FlipView[{#, Dynamic @ res[#]}]] &,
      expr,
      pat,
      (res[#] = #2) &,
      opts
    ]
  ]

myTracePrint[f[3], _f]
$\endgroup$
5
  • $\begingroup$ Impressively short! +1~ $\endgroup$
    – Wjx
    Sep 2, 2017 at 15:39
  • $\begingroup$ but there might be some bugs? What if the definition of f is like f[]:=RamdomReal[] and the traced expression is Array[f,10]? Then f[] may have different results in multiple calls . $\endgroup$
    – Wjx
    Sep 2, 2017 at 22:57
  • $\begingroup$ @Wjx Good point; I'll have to consider and address that. $\endgroup$
    – Mr.Wizard
    Sep 3, 2017 at 3:12
  • $\begingroup$ it seems that adding a FinishDynamic[] after res[#]=#2 would help? I haven't test it but in theory it would address this problem. $\endgroup$
    – Wjx
    Sep 8, 2017 at 4:35
  • $\begingroup$ There are 2 things that my more complicated function does that I really wanted. 1. It allows one to copy the expression that you see instead of the FlipView construct. 2. It allows one to burn in the results so that quitting the kernel and restarting doesn't lose the information about what the expressions evaluated to. $\endgroup$
    – Carl Woll
    Oct 2, 2017 at 20:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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