Is it possible to Print expressions in reverse order?
This is admittedly messy, but something along these lines might work:
insertBelowEvaluationCell[expr_] :=
(SelectionMove[EvaluationNotebook[], After, EvaluationCell];
NotebookWrite[EvaluationNotebook[], Cell[BoxData@ToBoxes[expr], "Print"]])
This function moves the insertion point just below the evaluation cell before inserting the text or expression to be printed.
Let's test it:
insertBelowEvaluationCell /@ Range[10]
Problems:
This messes with the insertion point in the notebook which can be modified interactively as well. Perhaps it's better to write the output into a separate notebook instead.
It does not work in command line mode (without a front end).
It's slow (noticeably slower than
Print
).
This is an arguably even messier solution than Szabolcs', and its performance isn't going to win any awards, but it has some (somewhat dubious) advantages:
- It allows you to choose whatever target you want for the printing, putting things in an arbitrary notebook, and the output will appear at the current selection in that notebook;
- After that, printing won't affect the current selection;
- You can have many different targets for printing;
- It provides another use for the seemingly useless
CellTags
feature!
The function in question creates a dummy cell that will eventually contain the output as a side-effect, and returns a closure that you call like Print
(more or less; it only takes one argument) in order to direct output to that target cell. The function is here:
Pillsy`UpsideDownPrinter[nb_: InputNotebook[]] :=
With[{
tag = ToString@FileHash[
StringToStream@ToString@{NotebookGet@nb, Date[]}]},
NotebookWrite[nb, Cell["", CellTags -> tag]];
Function[content,
Module[{nbExpr = NotebookGet[nb], pos},
pos = Position[nbExpr, Cell[___, CellTags -> tag, ___]];
NotebookPut[
MapAt[
Replace[#,
Cell[stuff_, opts___] :>
Cell[CellGroup[{Cell[content], Cell[stuff]}], opts]] &,
nbExpr,
pos],
nb]];]]
First, NotebookWrite
is used to create a Cell
in the target notebook with a unique CellTag
(which is what all the FileHash
stuff is for), and then we continually modify the target notebook's structure using standard Mathematica structural operations. The stuff with CellGroup
allows us to effectively create a "linked list" of generated cells in a (probably futile) stab at maintaining some degree of efficiency.
You use it like so:
printer = Pillsy`UpsideDownPrinter[];
printer["foo!"]; printer["bar!"]; printer["baz!"];
which will give output that looks like
"baz!" "bar!" "foo!"
It turns out that the ordering of the output of Print
cells is determined by whether the cells printed have the options CellAutoOverwrite
and GeneratedCell
both set to False
. If so, the cells are printed at the top (in reverse order), otherwise they are printed at the bottom.
In general, it is not easy to control whether the printed cells have these options set to False
using Print
. On the other hand, the mostly equivalent version using CellPrint
does not have this problem. So, using CellPrint
with appropriate options instead of Print
will print the cells in "reverse" order. However, having these options set to False
means the next time the input is evaluated, the cells will not be deleted.
This leads to the following function:
SetAttributes[ReversePrints, HoldAll];
ReversePrints[expr_] := Block[{Print=myprint, uuid=CreateUUID[]},
Internal`WithLocalSettings[
Null,
expr,
(* clean up *)
CurrentValue[Cells[CellTags->uuid], CellAutoOverwrite]=Inherited;
CurrentValue[Cells[CellTags->uuid], GeneratedCell]=Inherited;
]
]
Clear[myprint]
myprint[a__] := CellPrint @ Cell[
BoxData@MakeBoxes@SequenceForm[a],
"Print",
CellTags->uuid,
GeneratedCell->False,
CellAutoOverwrite->False
]
The function myprint
"print" cells with the desired options. It also includes a CellTags
option so that the desired options can be cleared afterwards.
Example:
ReversePrints[Do[Print[i], {i, 3}]]
3
2
1
One final note: CellPrint
is noticeably slower than Print
.