9
$\begingroup$

Today, I answered a question of mine that asked two month ago. Please see here

Now I would like to add the argument checking in this function. Then I used a method that Mr.Wizard answered

Requirement for the arguments of Bernstein[n,i,u]

  • n must be a integer like $1,2,3,...$;
  • i must be a integer like $1,2,3,...$;
  • i should between 0 and n-1.

For instance, the built-in BernsteinBasis gives the warning information as below:

BernsteinBasis[1.2, 2, 3]

BernsteinBasis::intnm: Non-negative machine-sized integer expected at position 1 in BernsteinBasis[1.2,2,3]. >>

BernsteinBasis[1.2, 2.1, 3]

BernsteinBasis::intnm: Non-negative machine-sized integer expected at position 1 in BernsteinBasis[1.2,2.1,3]. >>

BernsteinBasis::intnm: Non-negative machine-sized integer expected at position 2 in BernsteinBasis[1.2,2.1,3]. >>

BernsteinBasis[4, 5, u]

BernsteinBasis::invidx2:Index 5 should be a machine-sized integer between 0 and 4. >>

checkArgs

Attributes[checkArgs] = {HoldAll};
(*check the number of arguments*)
checkArgs [func_[args___]] /; Length@{args} != 3 := 
  Message[func::argrx, func, Length@{args}, 3]

(*check the type of the first arguments*)
checkArgs [func_[a_, b_, c_]] /; ! MatchQ[a, _Integer?NonNegative] := 
  Message[func::intnm, func[a, b, c], 1]

(*check the type of second arguments*)
checkArgs [func_[a_, b_, c_]] /; ! MatchQ[b, _Integer?NonNegative] := 
  Message[func::intnm, func[a, b, c], 2]

checkArgs[func_[a_, b_, c_]] /; ! (0 <= b <= a - 1) := 
  Message[func::invidx, b, 0, a - 1]

(*other valid cases*)
checkArgs[other_] := True

Main implementation

Bernstein::invidx = 
 "The index `1` should be a non-negative machine-sized integer betwwen `2` and `3`.";

 SetAttributes[Bernstein, {Listable, NHoldAll, NumericFunction}]
(*special cases*)

Bernstein[n_, i_, u_]?checkArgs /; i < 0 || i > n := 0

Bernstein[0, 0, u_]?checkArgs := 1

Bernstein[n_, i_, u_?NumericQ]?checkArgs := 
 Binomial[n, i] u^i (1 - u)^(n - i)

(*expansion of the basis of Bernstein*)
Bernstein /: PiecewiseExpand[Bernstein[n_, i_, u_]] := 
 Piecewise[
  {{Binomial[n, i] u^i (1 - u)^(n - i), 0 <= u <= 1}, 
   {0, u > 1 || u < 0}}]

(*the derivatives of the basis of Bernstein*)
Bernstein /: Derivative[0, 0, k_Integer?Positive][Bernstein] := 
 Function[{n, i, u}, 
  D[
   n (Bernstein[n - 1, i - 1, u] - Bernstein[n - 1, i, u]), 
   {u, k - 1}]
 ]

However, it gives the following information.

$RecursionLimit::reclim: Recursion depth of 256 exceeded. >>

$RecursionLimit::reclim: Recursion depth of 256 exceeded. >>

$RecursionLimit::reclim: Recursion depth of 256 exceeded. >>

General::stop: Further output of $RecursionLimit::reclim will be suppressed during this calculation. >>

Bernstein::intnm: Non-negative machine-sized integer expected at position >Bernstein[n_,i_,u_] in 1. >>


Update

Thanks for Mr.Wizard's revision that adding HoldForm in checkArgs to remove the recursion.

In addition, Mr.Wizard given me a hint that ultilizing the Message as a side-effect in the comment

Now I have a reference here

SyntaxInformation[f] = {"ArgumentsPattern" -> {_}};
f[1] := True
f[_] := False
f[x___] /; Message[f::argx, "f", Length@{x}] := Null

Additional, The Toad has a comment as below:

I just remembered why I don't use this in my packages... if you have different messages being thrown based on the form of the input (as I often have), then throwing messages as a side-effect of not matching the form will result in all messages being thrown

However, this demo just for one argument, and when the number of argument greater than $1$, I have any idea to deal with Message with side-effect.

$\endgroup$
  • $\begingroup$ @SjoerdC.deVries, I know the built-in BernsteinBasis applied the UpValues method. For instance, D[BernsteinBasis[5, 3, u], {u, 2}]. In addition, BernsteinBasis[3., 1., u] gives the warning information normally. $\endgroup$ – xyz Jul 22 '15 at 15:13
  • $\begingroup$ @Mr.Wizard, In this case, I used your "Fall-through method " , however, it failed. Could you help me if you have time? $\endgroup$ – xyz Jul 23 '15 at 0:59
  • $\begingroup$ @ShutaoTang @name notifications only work of a user has already commented on the post therefore I did not see your notice. However I saw this now due to the recent edit. If I have time today I shall attempt to answer this. $\endgroup$ – Mr.Wizard Jul 23 '15 at 12:15
  • $\begingroup$ @Mr.Wizard, Thanks a lot.:-) Now I am trying to apply your methods that hand error-message in my functions. However, this time I failed and I didn't why. $\endgroup$ – xyz Jul 23 '15 at 12:21
4
+100
$\begingroup$

I have given your code only a cursory read but I think I spotted a (the?) problem: your Message code uses an unheld equivalent of the test expression itself. This cannot work. If the expression would generate a Message the first time it would even within Message and you will get infinite recursion. Use HoldForm to prevent this:

(*check the type of the first arguments*)
checkArgs[self : func_[a_, b_, c_]] /; ! MatchQ[a, _Integer?NonNegative] := 
 Message[func::intnm, HoldForm[self], 1]

(*check the type of second arguments*)
checkArgs[self : func_[a_, b_, c_]] /; ! MatchQ[b, _Integer?NonNegative] := 
 Message[func::intnm, HoldForm[self], 2]

With this correction your code no longer produces a recursion error on definition, however I get:

Bernstein::intnm: Non-negative machine-sized integer expected at position 1 in Bernstein[n_,i_,u_]. >>

I believe this comes from the limited evaluation that takes place during function definition and I think the definition will still be made correctly, but I'll have to check that later.

Separately I think you can and probably should be including checkArgs in the TagSet definition:

Bernstein /: PiecewiseExpand[Bernstein[n_, i_, u_]?checkArgs] := . . .

However again I haven't made an attempt to test this definition itself. If you have additional problems please note them in the question and I shall try to help when I return to this.

$\endgroup$
  • $\begingroup$ +1,THX, Now I utilize the followng cases: Bernstein[3.2, 1, u] ,Bernstein[3, 1.2, u] and Bernstein[5, 6, u] to test the funtion Bernstein and then they give the desired error informtion respectively. However, I would like to how inplement the checkArg to make the Bernstein[3.2, 1.2, u] return two error-message like BernsteinBasis[3.2, 1.2, u] $\endgroup$ – xyz Jul 23 '15 at 13:45
  • $\begingroup$ @Shutao If you want more than one error message you can issue the Message as a side-effect rather than a match on checkArgs. The evaluator would then try all of the checks rather that stopping after the first Message. However if you want all messages to fire I think it is actually better or at least easier to make the definition upon the primary function (Bernstein). The methods I described were posted in response to the implied desire not to issue multiple messages. $\endgroup$ – Mr.Wizard Jul 23 '15 at 14:15
  • $\begingroup$ The Toad's comment: throwing messages as a side-effect of not matching the form will result in all messages being thrown. But , for me, I don't know how to implement this(throwing messages as a side-effect). So could you give me a demo by this Bernstein case? ThX! $\endgroup$ – xyz Jul 23 '15 at 14:35
  • 3
    $\begingroup$ @Shutao FYI I am not ignoring you; I've just got other things to work on too. $\endgroup$ – Mr.Wizard Jul 24 '15 at 11:58

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.