16

I usually check my intermediate variable by Echo.Like

Table[Echo@x, {x, 10}]

But when the intermediate variable have too much value,the Font-End often crash,such as Table[Echo@x, {x, 10^3}].So I want to make a custom myEcho,which always show the first 5 results when I run Table[myEcho@x, {x, 10}] every time.Like

Table[myEcho@x, {x, 10}]

Or we can specify the times that we hope,such as Table[myEcho[x,3], {x, 10}] will show the first 3 results


As the Kuba's fix,this is current method.

Clear[fun, myEcho]
fun[n_: 5] := 
 Module[{times = 0}, (times++; If[times <= n, Echo, Identity][#]) &]

myEcho = fun[5];
Table[myEcho@x, {x, 10}]

But the weakness is obvious still,which mean I should run myEcho = fun[5];Table[myEcho@x, {x, 10}] every time.Actually I hope get a same result when I just run Table[myEcho@x, {x, 10}].

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
yode
  • 26,686
  • 4
  • 62
  • 167
  • 1
    If I understood the question correctly, since 2021, years after this question, there is a resource function that limits the number of outputs from Echo named LimitEcho : https://resources.wolframcloud.com/FunctionRepository/resources/LimitEcho/ – userrandrand Dec 12 '22 at 20:11
  • 1
    I should mention that I have not yet used it myself so I do not know how well it works. – userrandrand Dec 12 '22 at 22:00
  • @userrandrand Yes, but is very very very slow, you can test ResourceFunction["LimitEcho"][ImageApply[Echo[#]&,ExampleData[{"TestImage","Lena"}]],5] – yode Dec 13 '22 at 03:15
  • 1
    I checked and yep it seems like the echos are still being produced as there is a button to show all the echos that occurred. That might also lead to problems with memory not sure. Why not c = 0; ImageApply[If[c < 5, c++; Echo[#], #] &, ExampleData[{"TestImage", "Lena"}]] ? That could be tedious to reset c=0 each time we need it for a new code. – userrandrand Dec 13 '22 at 17:01

4 Answers4

10

Here is how I would approach this:

Clear[myEcho, myEchoCounter]
myEcho::stop = "Further output from `` will be suppressed during evaluation of In[``].";
myEcho[, ] := myEcho[];
myEcho[label_: Null, max_: 3][expr_] /; myEchoCounter[$Line, label] >= max := expr
myEcho[label_: Null, max_: 3][expr_] := (
   If[Not@NumberQ[myEchoCounter[$Line, label]], myEchoCounter[$Line, label] = 0];
   If[myEchoCounter[$Line, label] < max, 
    If[MatchQ[label, Null | None], Echo[expr], Echo[expr, label]]];
   If[++myEchoCounter[$Line, label] == max, 
    Message[myEcho::stop, "myEcho"[label, max], $Line]];
   expr);

Now

(myEcho["Before Table"][x]; Table[myEcho["Table"][x], {x, 10}]; myEcho["After Table"][x])

(Table[myEcho[, 3][x], {x, 10}]; Table[myEcho[, 6][x], {x, 10}])

screenshot

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
  • This is solution near to perfect,I will accept it after 36h if there is no better answer. – yode Mar 23 '17 at 12:22
  • Just when we don't set any label or we set a same label,we cannot get the result we want,I don't sure whether $ModuleNumber can help or not. – yode Mar 23 '17 at 12:52
  • 1
    @yode What you call "unexpected" is actually "by design": on the same $Line myEcho with the same label (or without a label) prints no more than max (by default 3) times. – Alexey Popkov Mar 23 '17 at 12:56
  • So I just wonder we can add a $ModuleNumber after our label but we don't show that $** – yode Mar 23 '17 at 13:03
  • 1
    Adding this code will automate the labelling process for Echo. $Pre =.; $Pre = Function[u, Module[{c = 0}, (Hold[u]) /. HoldPattern[Echo[a__]] :> RuleCondition[Inactive[myEcho][++c][a]]] // Activate // ReleaseHold, HoldAll]; – vapor Mar 23 '17 at 13:10
  • @yode As Kuba correctly say, you should decide for yourself at what moment of evaluation myEcho should be suppressed. From your comments it looks like you wish to have the counter to be reset for every Table on the same $Line. It is possible but difficult to achieve (AFAICS it requires analysis of Stack[]), and at the same time with this approach you won't know from where the myEcho was printed (because they'll have identical labels). My approach is to assign different label to every myEcho and bind the counter to the label and $Line. – Alexey Popkov Mar 23 '17 at 13:11
  • @yode Actually with my current implementation what you showed is possible, try Table[myEcho[, 3][x], {x, 10}]; Table[myEcho[, 6][x], {x, 10}]. – Alexey Popkov Mar 23 '17 at 13:58
  • Thanks for so many labors for it.And it's seem it is the best solution still. – yode Mar 23 '17 at 23:09
6

Another way to do this:

In[1]:= << GeneralUtilities`

In[2]:= $MaximumEchoRate = 5;

In[3]:= Table[EchoHold@x, {x, 10}]

x \[Function] 1

x \[Function] 2

x \[Function] 3

x \[Function] 4

x \[Function] 5

During evaluation of In[3]:= Maximum echo rate exceeded, change $MaximumEchoRate to adjust.

Out[3]= {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
M.R.
  • 31,425
  • 8
  • 90
  • 281
4

Update

I figured that I can make the code much faster (close to the speed without any Echo) if I can get rid of all the if's and let the pattern matcher(which is pretty fast) do all the heavy work for me.

BeginPackage["MyEcho`"];

MaxEchoCount=5; MyEcho::stop="Max Echo count of reached, further will be suppressed during evaluation of In[``]."; SetAttributes[MyEcho,HoldAllComplete]

Module[{list={}}, MyEcho[label_,return_,subexpr___]:= Which[ list[[label]]<MyEchoMaxEchoCount,list[[label]]++;Echo[return,subexpr], list[[label]]==MyEchoMaxEchoCount,Message[MyEcho::stop,MyEcho`MaxEchoCount,Defer[Echo[return,subexpr]],$Line];MyEcho[label,return1_,subexpr1___]:=return1;return];

$Pre=Function[{expr}, Quiet@Cases[DownValues[MyEcho],HoldPattern[MyEcho[label_Integer,e___]]:>Unset[MyEcho[label,e]],Infinity]; list=ConstantArray[0,Length@list]; ReleaseHold[Hold[expr]/. HoldPattern[Echo[subexpr__]]:>With[{c=(AppendTo[list,0];Length@list)},MyEcho[c,subexpr]/;True]],HoldAll]; ];

EndPackage[];

The key here is that I will add a narrower definition to MyEcho once it reached the maximum echo count, so the next time it meets the same function at the same place, it will hit the new function definition and directly return.

One can compare this version to the old version, as well as the version with QuietEcho wrapped around the whole expression with the following code:

Table[Flatten[{{Table[Echo[x], 2], Echo[10 - x]}, {Sqrt[2]}}], {x, 1000000}]

The results are:

  • Without Echo: 1.84s
  • With QuietEcho wrapped around everything: 2.76s
  • With the new definition: 2.42s
  • With the old definition: 5.61s

Old version

crit = 2;
SetAttributes[MyEcho, HoldAll]
Module[{res},MyEcho[label_, return_, subexpr___] :=
  If[NumberQ[label],If[label >= crit, return,
    label++; res = Echo[return, subexpr];
    If[label == crit, Print["Already " <> ToString[crit] <> " Echo here!\nNo more Echo allowed!!!!"]];res],
  label = 1; Echo[return, subexpr]]]

$Pre = Function[{expr}, ReleaseHold[Hold[expr] /. HoldPattern[Echo[subexpr__]] :> With[{u = Unique[]}, MyEcho[u, subexpr] /; True]], HoldAll]

Similar results, but a bit more integrated and more carefully protected.

IMG

Note that one can still save the old $Pre by adding it in front of this code.(one must limit the functionality of old $Pre to an extent that full evaluation of expressions is forbidden)

Wjx
  • 9,558
  • 1
  • 34
  • 70
-1

I think this is probably slower than Kuba's solution in comments, but does

myEcho[x_,n_:5]:=If[x<=n,Echo[x],Identity[x]];

Table[myEcho@x,{x,10}]

not achieve what you're looking for?

  • Oh,we can judge the x<=n in the general case when we debug.such as Table[myEcho[x^2, 3], {x, 10}] just give one result,but it is sure a good think.. – yode Mar 23 '17 at 23:20
  • This is not what OP wants. n should be the number of times Echo prints, not the upper limit of the number Echo prints. – vapor Mar 24 '17 at 06:39
  • Ah, sorry, misunderstood what you wanted. How about myEcho[fx_, x_, n_: 5] := If[x <= n, Echo[fx], Identity[fx]], with usage Table[(x^2)~myEcho~x, {x, 10}]? – AnotherShruggingPhysicist Mar 24 '17 at 11:19
  • @AnotherShruggingPhysicist What about Table[(x^2)~myEcho~x, {x, 10, 20}]? You cannot assume Table or a iterator like x exists. Even you can, you are also assuming the iterator starting with 1. If you insist on similar approach and remove this x parameter, the best you can achieve is probably the code in the last section of the question. – vapor Mar 24 '17 at 14:54