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.