2
$\begingroup$

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

$\endgroup$
7
  • $\begingroup$ You could try CellPrint. $\endgroup$
    – lericr
    Aug 9, 2022 at 14:51
  • $\begingroup$ 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? $\endgroup$
    – lericr
    Aug 9, 2022 at 14:53
  • $\begingroup$ @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. $\endgroup$
    – yurie
    Aug 9, 2022 at 14:57
  • $\begingroup$ @lericr so that I can comparing the results against different $globalvariable[] conveniently. $\endgroup$
    – yurie
    Aug 9, 2022 at 15:01
  • 1
    $\begingroup$ This recent Q&A might be of interest to you: mathematica.stackexchange.com/questions/271105 - it is about how to take a notebook (such as yours) and conveniently run it for many inputs $\endgroup$
    – Lukas Lang
    Aug 9, 2022 at 15:36

2 Answers 2

3
$\begingroup$

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.

$\endgroup$
14
  • $\begingroup$ Thx! I have add more descriptions in the question. $\endgroup$
    – yurie
    Aug 9, 2022 at 15:44
  • 1
    $\begingroup$ Thanks, but I don't understand your update. $\endgroup$
    – lericr
    Aug 9, 2022 at 15:48
  • $\begingroup$ 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. $\endgroup$
    – lericr
    Aug 9, 2022 at 15:55
  • $\begingroup$ 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. $\endgroup$
    – lericr
    Aug 9, 2022 at 15:56
  • $\begingroup$ 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 :< $\endgroup$
    – yurie
    Aug 9, 2022 at 16:03
0
$\begingroup$

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

$\endgroup$

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.