Skip to main content

programming - Checking the argument of user-defined function with side-effect method


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.




Comments

Popular posts from this blog

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...