2

Suppose I have a long notebook with three parts:

Remove["Global`*"];

(global settings) $globalvariable[]=1;

(main functions) foo[]:=Print@$globalvariable[];

(collecting the results) foo[]

Each time using this notebook, I just change the global settings and then Evaluate Notebook. The problem is that the Output[] in the third part will be overwritten, and to prevent this behaviour I need to add a new line by hand between foo[] and Output[].

Is there a method of preventing overwriting the output only for foo[] without modifying any behaviour of foo[]?

I tried wrapping foo[] with Button

Button["test",foo[]]

as in this question but failed:

  • when foo[] is complicated containing Print, Message, Timing, etc, Button["test",foo[]]'s result is different from foo[];

  • when Button["test",foo[]] is re-evaluated, the results generated by the button will be cleared.

I think there should be more direct approach but am not familiar with the front-end issues.


update 1

The situation is that when I am developing some package, this notebook serves as a pre-package before integrating the codes into *.wl. I need to modify the global settings and main functions according to the outputs.

I think the current comments and answers are suitable for the final stable version of the codes. For this situation there are certainly indirect approaches like:

  • packing all the relavent codes into a single function;
  • integrating the relavent codes into a package or a notebook
  • etc...

update 2: more background

The number of global variables and main functions of the notebook is ~100. The simplest function (three-point in 1-dim) is like the followings. As you can see, tracking all symbols across functions is inconvenient.

The current method I used is saving all the codes into one notebook init1.nb, and working in another notebook with

Module[{path,notebook},path=NotebookDirectory[]<>"init1.nb";
notebook=NotebookOpen[path,CellContext->"Global`"];
FrontEndExecute[FrontEndToken[notebook,"SelectAll"]];
FrontEndExecute[FrontEndToken[notebook,"Evaluate"]]
]

Example codes:

threePoint[x1_, x2_, 
   x3_, \[CapitalDelta]1_, \[CapitalDelta]2_, \[CapitalDelta]3_] := \
(x1 - x2)^(-\[CapitalDelta]1 - \[CapitalDelta]2 + \[CapitalDelta]3) \
(x2 - x3)^(\[CapitalDelta]1 - \[CapitalDelta]2 - \[CapitalDelta]3) \
(-x1 + x3)^(-\[CapitalDelta]1 + \[CapitalDelta]2 - \[CapitalDelta]3) \
;
threePointCoefficient[rank1_, rank2_, rank3_, i1_, i2_, i3_] := 
  0 /; (i1 <= 0 || i1 > rank1) || (i2 <= 0 || 
      i2 > rank2) || (i3 <= 0 || i3 > rank3);
threePointCoefficient[rank1_, rank2_, rank3_, i1_, i2_, i3_] := 
  sub[symbolThreePointCoefficient, i1, i2, i3];
threePointFactor[{rank1_, rank2_, rank3_, i1_, i2_, i3_}, k1_, k2_, 
   k3_] := \!\(
\*UnderoverscriptBox[\(\[Sum]\), \(n1 = 0\), \(rank1 - i1\)]\(
\*UnderoverscriptBox[\(\[Sum]\), \(n2 = 0\), \(rank2 - i2\)]\(
\*UnderoverscriptBox[\(\[Sum]\), \(n3 = 0\), \(rank3 - i3\)]
\*FractionBox[\(
\*SuperscriptBox[\(k1\), \(n1\)]\ 
\*SuperscriptBox[\(k2\), \(n2\)]\ 
\*SuperscriptBox[\(k3\), \(n3\)]\), \(\(n1!\)\ \(n2!\)\ \(n3!\)\)]\ \
threePointCoefficient[rank1, rank2, rank3, i1 + n1, i2 + n2, 
       i3 + n3]\)\)\);
threePointFactor2[{rank1_, rank2_, rank3_, i1_, i2_, i3_}, x1_, x2_, 
   x3_] := 
  threePointFactor[{rank1, rank2, rank3, i1, i2, 
    i3}, -Log@ratioX[x2, x3, x1], -Log@ratioX[x3, x1, x2], -Log@
     ratioX[x1, x2, x3]];
threePoint[{rank1_, rank2_, rank3_, i1_, i2_, i3_}, x1_, x2_, 
   x3_, \[CapitalDelta]1_, \[CapitalDelta]2_, \[CapitalDelta]3_] := 
  threePoint[x1, x2, 
    x3, \[CapitalDelta]1, \[CapitalDelta]2, \[CapitalDelta]3] \
threePointFactor[{rank1, rank2, rank3, i1, i2, 
     i3}, -Log@ratioX[x2, x3, x1], -Log@ratioX[x3, x1, x2], -Log@
      ratioX[x1, x2, x3]];

enter image description here

Lacia
  • 2,253
  • 3
  • 19
  • You could try CellPrint. – lericr Aug 09 '22 at 14:51
  • Are you trying to get some sort of history? Does it need to span sessions? Could you just use some aggregation structure instead of relying on display? – lericr Aug 09 '22 at 14:53
  • @lericr I'm sorry that I don't know the things you mentioned. I want the following behaviour: each time running foo[] will generate a new cell storing the messages, prints and outputs, instead of overwritting the latest ones. – Lacia Aug 09 '22 at 14:57
  • @lericr so that I can comparing the results against different $globalvariable[] conveniently. – Lacia Aug 09 '22 at 15:01
  • Use a Table, e.g., Table[{var, f[var]}, {var, {val1, val2, val3}}] // Grid or if the variable values fit a pattern use a standard iterator ({var, start, end, step}) in the Table – Bob Hanlon Aug 09 '22 at 15:26
  • 1
    This recent Q&A might be of interest to you: https://mathematica.stackexchange.com/questions/271105/ - it is about how to take a notebook (such as yours) and conveniently run it for many inputs – Lukas Lang Aug 09 '22 at 15:36
  • @LukasLang Thx! That is quite similar to my question. I'll try finding some solution suitable for mine. – Lacia Aug 09 '22 at 16:50

2 Answers2

3

so that I can comparing the results against different $globalvariable[] conveniently

Rather than answer your initial question directly, I'm going to try to address this objective from your comment. And I apologize for getting philosophical. People often "just want the answer to my question", but they can't see that there are better possibilities around the corner if they would just shift their perspective a bit.

Don't use global data. Or at least, don't use it this way. Let's make a better foo to illustrate:

foo[] := Print@$globalvariable[] (*bad form, subtle dependencies, difficult to test*)
betterFoo[arg_] := Print[arg] (*better form, no magic, but no output that we can get our hands on (Print is a side-effect)*)
bestFoo[arg_] := arg (*we now have an actual thing that we can store, manipulate, compose, etc.*)

Now, bestFoo looks ridiculous because it does nothing to its argument, but I'm assuming this was just an example, so let's say it's something more interesting like:

bestFoo[arg_] := {arg^2, Sin[arg], 17/arg}

At this point, let's say you still have your $globalvariable[] function. Instead of foo[] you'd use bestFoo[$globalvariable[]]. But you can also do bestFoo[5]. That is, you're now no longer dependent on your global data--you could compare bestFoo[5] with bestFoo[10] immediately and directly without figuring out how to update your global data.

Now, let's say you want to "conveniently compare the result using different data" (paraphrased). Let's say the data you want to apply foo/bestFoo to are {1,10,100}:

AssociationMap[bestFoo, {1, 10, 100}]
(* 
this gives:
<|1 -> {1, Sin[1], 17}, 10 -> {100, Sin[10], 17/10}, 100 -> {10000, Sin[100], 17/100}|>
*)

Now you have a structure holding the results of your various "runs". You don't need to rely on the front end display to see your history. You could save this to a file. You can keep adding to it over time. You can do more computational comparative analyses across different runs.

The objection here is most likely going to be "but the global data is complicated, the foo is complicated, this answer works for these simplisitic examples, but that's not what my real data looks like". Feel free to add more detail to what your data looks like, what foo really does, what your context is, and what your actual objective is, and we can help you find a path to what will work for you.

lericr
  • 27,668
  • 1
  • 18
  • 64
  • Thx! I have add more descriptions in the question. – Lacia Aug 09 '22 at 15:44
  • 1
    Thanks, but I don't understand your update. – lericr Aug 09 '22 at 15:48
  • Specifically, I don't know what you expect from a "pre-package", I don't know what the "outputs" are that you use to adjust your "global settings" and "main functions", and I don't know why those outputs need to be displayed without overwriting. I'm thinking that you just have a workflow that I'm entirely unfamilar with, but you're assuming that it's so obvious that it doesn't need to be described. – lericr Aug 09 '22 at 15:55
  • It all sounds very confusing and disorganized to me. I'll go back to "don't use global data", because I suspect that that is the root of your difficulties. – lericr Aug 09 '22 at 15:56
  • sorry that I cannot provide the complete codes here. Tracking all the relavent symbol dependencies across the functions seems impossible for me, since there are roughly tens of local variables and global ones sending from one function into another, and there are roughly one hundred functions in one notebook. It's impossible to recontruct them :< – Lacia Aug 09 '22 at 16:03
  • The simplest example is a six-point function in four-dimension, there are 6x4 local variables as the six points, and tens of global ones labeling the operators. – Lacia Aug 09 '22 at 16:07
  • Yeah, I assumed that there would be complexity. I don't need the details of your data or your functions. I'm trying to understand what you're objective actually is. You started with wanting evaluations to not overwrite previous outputs, but it sounds like what you really want is an efficient process for developing packages. That's a completely different objective that in all likelihood will not require what you originally asked for. – lericr Aug 09 '22 at 16:51
  • And now I see that you're using FrontEndExecute to evaluate the contents of a notebook. This all just seems very indirect and awkward. Why don't you just use Get or proper packages? – lericr Aug 09 '22 at 16:53
  • As you can see, tracking all symbols across functions is inconvenient. No, I don't see that at all. If you have 100 parameters, then you have 100 parameters. Making them global variables doesn't magically reduce the complexity. Maybe my comments have just de-railed this whole thing. Sorry to have added to the confusion. – lericr Aug 09 '22 at 16:58
  • In the earlier attemps several months ago, I tried integrating them into a package. But sadly some functions depend on the Context and without careful reconstruction, they cannot be moved into package simply. As you noticed, these issues are away from the original request of this question. This question is about how to preserve the old outputs, which I'm not familiar with. No problem, any comment is welcomed, since I can learn more things from the discussion. – Lacia Aug 09 '22 at 16:59
  • My only suggestion is to preserve the outputs as actual data, rather than relying on the notebook display mechanisms. – lericr Aug 09 '22 at 17:19
  • Using CellPrint I wrote an answer that partially solve this problem. Do you know how to add CellLabels properly when CellPrint? I also tried NotebookWrite, but NotebookWrite doesn't accept ExpressionCell. – Lacia Aug 09 '22 at 19:55
  • CellLabel and CellTags are just options for Cell (and anything that evaluates to Cell, like TextCell). You may also need the related Show* options. For example: CellPrint[TextCell["hello", CellLabel -> "wonder woman", CellTags -> {"superman"}, ShowCellTags -> True, ShowCellLabel -> True]]. – lericr Aug 09 '22 at 21:46
  • Many thx! ShowCellLabel is exactly what I missed. – Lacia Aug 09 '22 at 23:29
0

As suggested by @lericr, I tried the functions related to CellPrint and read tutorial/ManipulatingNotebooks. With some efforts I'm satisfied with the following codes:

rerun//Attributes={HoldAllComplete};
rerunCounter[___]:=0;
rerunCollector[___]:={};
rerunCache[]={};

rerun[codes___][collector_String]:= Block[{Print},Module[{celllsit,counter}, (set the counter wrt. collector) rerunCounter[collector]++; counter=rerunCounter[collector]; rerunCollector[]=joinUnique[rerunCollector[],{collector}]; rerunCollector[collector]=Join[rerunCollector[collector],{counter}]; (preprint messages) celllsit["preprint"]={ ExpressionCell[ StringTemplate["Current run: at"][counter,collector], "Output", ShowCellTags->True, ShowCellLabel->True ], ExpressionCell[ DateString[{"ISODate"}], "Output", rerunCellOptions["Date",counter] ] }; (store the Print messages by cells) celllsit["print"]={}; Print[exprs___]:=rerunFunPrint[celllsit["print"],exprs]; (evaluation of codes) rerunCache[collector,counter]={codes}; (* the results*) celllsit["eval"]=Table[ ExpressionCell[ coderesult, "Output", rerunCellOptions["Run",counter] ], {coderesult,rerunCache[collector,counter]} ]; celllsit["total"]=Join[celllsit["preprint"],celllsit["print"],celllsit["eval"]]; CellPrint@celllsit["total"]; ]];

rerunFunPrint//Attributes={HoldFirst}; rerunFunPrint[celllist_,exprs__]:= AppendTo[celllist, ExpressionCell[ Row[{exprs}], "Print", CellAutoOverwrite->False ] ]; rerunCellOptions[label_String,counter_Integer]:= { CellAutoOverwrite->False, CellLabel->StringTemplate["[]= "][label,counter], CellLabelAutoDelete->False, ShowCellTags->True, ShowCellLabel->True }; rerunCacheClear[collector_String]:= ( Table[ rerunCache[collector,$$counter]=., {$$counter,rerunCounter[collector]} ]; rerunCounter[collector]=.; rerunCollector[collector]=.; rerunCollector[]=complement[rerunCollector[],{collector}]; ) rerunCacheClear[]:= ( rerunCacheClear/@rerunCollector[]; )

For the following test function, the results are,

a=1;
f[___]:=(Print@a;Print[x,y];a++;a)

enter image description here Using rerunCacheClear can clear the stored results: enter image description here

Lacia
  • 2,253
  • 3
  • 19