12
$\begingroup$

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.)

$\endgroup$
  • 3
    $\begingroup$ 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). $\endgroup$ – kglr Aug 24 '14 at 18:51
  • 2
    $\begingroup$ Closely related: (2459) $\endgroup$ – Mr.Wizard Aug 27 '14 at 5:22
4
$\begingroup$

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]
$\endgroup$
  • 1
    $\begingroup$ Warning: messing with $MessageList might break unit testing tools that can test messages. $\endgroup$ – Szabolcs Sep 13 '15 at 11:26
3
$\begingroup$

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
$\endgroup$
0
$\begingroup$

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.

$\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.