13

I have written code such that if it runs into a particular function, a warning message is issued. However, it is common for the program to call this function several times in a single evaluation, but the warning message need be issued only once.

How do you limit the number of times a message is issued per evaluation?

Here is an example:

 Clear[badness];
 ClearAll[badness];
 badness::oops = "Watch out!  Result could be misinterpreted.";
 badness[0] := CompoundExpression[Message[badness::oops]; 0]

 Clear[reduction]
 f[0] = 0;
 reduction = {f[x_] :> x + f[x - 1] + badness[0]};

So, If the user runs the following:

 f[5] //. reduction

we get:

output of badness

Is there a way to limit the number of times (without also getting the General::stop) the message badness:oops is given to the user? (In the particular case for my code, I would like to limit it to just one time.)

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
QuantumDot
  • 19,601
  • 7
  • 45
  • 121
  • 3
    Good question...I would have expected that CurrentValue[$FrontEndSession, {MessageOptions, "MaxMessageCount"}] = 1; or SetOptions[$FrontEndSession, {MessageOptions -> {"MaxMessageCount" -> 1}}] would the job , but it doesn't -- you still get 3 messages before General::stop kicks in :( -- Version 9.0.1.0 Windows 8 64bit). – kglr Aug 24 '14 at 18:51
  • 2
    Closely related: (2459) – Mr.Wizard Aug 27 '14 at 05:22
  • This answer contains a modern implementation. – Alexey Popkov Jan 27 '22 at 18:59

3 Answers3

4

It's been a day and no has posted a truly elegant, natural, and general solution, so I'll post a narrow one.

By default, a message is stopped when it occurs three times in $MessageList. One can reduce the number of times a message is printed by pre-loading $MessageList with it.

Block[{$MessageList},
 Unprotect[$MessageList];
 $MessageList = {HoldForm[badness::oops], HoldForm[badness::oops]};
 f[5] //. reduction
 ]

badness::oops: Watch out! Result could be misinterpreted.

General::stop: Further output of badness::oops will be suppressed during this calculation. >>

(* 15 *)

To have it happen automatically every time, use $Pre:

SetAttributes[foo, HoldAll];
foo[new_] := Block[{$MessageList},
  Unprotect[$MessageList];
  $MessageList = {HoldForm[badness::oops], HoldForm[badness::oops]}; 
  new]
$Pre = foo;

Or one could put the code in a function:

ClearAll[almostOff];
SetAttributes[almostOff, HoldAll];
almostOff[code_, msgs__] := Block[{$MessageList},
  Unprotect[$MessageList]; $MessageList = List @@ HoldForm /@ Hold[msgs, msgs];
  code
  ]

almostOff[f[5] //. reduction, badness::oops]
Michael E2
  • 235,386
  • 17
  • 334
  • 747
4

Here is a method using Off and On triggered by the first appearance of the message.

  • I avoid unprotecting any System Symbols by using TagSetDelayed.

  • I use $Post to turn the message back on.

  • Modification of your function is not required; this operates via the low-level Message system.

Code:

badness::oops = "Watch out!  Result could be misinterpreted.";

badness /: p : MessagePacket[badness, "oops", _BoxData] /; msgLimit =!= True :=
 Block[{msgLimit = True}, Off[badness::oops]; p]

$Post = (On[badness::oops]; #) &;

Now:

badness[0] := (Message[badness::oops]; 0)

f[5] //. {f[0] :> 0, f[x_] :> x + f[x - 1] + badness[0]}

badness::oops: Watch out! Result could be misinterpreted.

15

And again:

f[5] //. {f[0] :> 0, f[x_] :> x + f[x - 1] + badness[0]}

badness::oops: Watch out! Result could be misinterpreted.

15
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
0

This is a bit ugly, but it seems to do what you want.

ClearAll[badness];
badness::oops = "Watch out!  Result could be misinterpreted.";
badness[0] /; badness[] := (Message[badness::oops]; badness[] = False; 0);
badness[_] := 0

Clear[reduction]
f[0] = 0;
badness[] = True;
reduction = {f[x_] :> x + f[x - 1] + badness[0]};

All it does it keep track of whether it has shown the message or not. If badness[] is True it shows the message and sets badness[] = False. This could easily be adapted to keep a count. The major drawback is that you have to manually set badness[] = True to show the message again.

wxffles
  • 14,246
  • 1
  • 43
  • 75