8
$\begingroup$

Suppose I have defined the following function inside a package that can take 2 arguments.

f[x_,y_]:={x,y}

So, if the user gives f[1,2] then the output is {1,2}. But if the input is f[1,2,3] then the output is also f[1,2,3].

I want to print an error message when the input is f[1,2,3], but I can't figure out how to count the number of arguments given by the user.

$\endgroup$
1

2 Answers 2

13
$\begingroup$

You can redefine your function f to accept a variable number of arguments (x__) and then check the length with Length.

Clear[f];
f::argerr = "Two arguments expected, but `1` given.";
f[x__] := 
  If[Length@List@x == 2, List@x, 
   Message[f::argerr, Length@List@x]; $Failed];

f[1, 2]
(* {1, 2} *)

f[1, 2, 3]
(* f::argerr: Two arguments expected, but 3 given. *)
(* $Failed *)

Another, probably a more appropriate way, as suggested in the comment by Jason, is to use CheckArguments.

Clear[f];
f[x_, y_] := {x, y};
f[args___] := Null /; CheckArguments[f[args], 2]

f[1, 2]
(* {1, 2} *)

f[1, 2, 3]
(* f::argrx: f called with 3 arguments; 2 arguments are expected. *)
(* f[1, 2, 3] *)
$\endgroup$
2
$\begingroup$

Why create an error if you already have the necessary arguments to proceed?

It seems everywhere I look coders especially new ones are eager to throw errors even when there are still easy workarounds. The end-user may not even be a programmer. Think of them as your customers. They may not think too kindly with using applications that are too sensitive to errors instead of self-correcting when possible.

My preferred approach is to always avoid error when possible. Here we can drop (not ignore) trailing arguments from evaluation but the end-user must always be alerted (warned) so he can decide if the output is still useful. A nice user-friendly way is to alert with an Echo[warning message] which looks very similar to error messages. These are hard to ignore but we can still proceed with evaluation.

Notice how much cleaner the code is now and as a bonus it is self-correcting. That is a win-win for everyone. You put out a better product and code is easier to manage.

1. A Good Approach: 1 Definition

Remove[f];
f[x_, y_, z___] := Block[
   {
    msg="2 args expected pos 3 not included in evaluation."
    },
   If[Length@{z} > 0, Echo@msg];
   {x, y}
   ];

f[1, 2, 3]

{1, 2}

>> 2 args expected pos 3 not included in evaluation .

2. Even Better: Recursive Calls

(* definitions *)
Remove@f;
f[x_, y_, 
   z__] := (Echo[{z}, "2 args expected, pos 3 dropped \[Rule]"]; 
   f[x, y]);
f[x_, y_] := {x, y};

(* input *)
seq = Range[1, 5] /. List -> Sequence;
f[seq](* this is just f[1,2,3,4,5] *)
f[a, b]
f[0, 0]

(* output *)
>>2 args expected, pos 3 dropped ->  {3,4,5}

{1,2}

{a,b}

{0,0}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.