Group Abstract Group Abstract

Message Boards Message Boards

0
|
3.8K Views
|
4 Replies
|
6 Total Likes
View groups...
Share
Share this post:

Default values for arguments of functions

Hi!

POSTED BY: Vladimir Ivanov
4 Replies

Hans Milton, thank you for your example, it works.

Eric Rimbey, I use Mathematica 9. Concerning you opinion on default values, I have to think it over.

POSTED BY: Vladimir Ivanov
Posted 2 years ago

This is a bit tangential, but challenges with default values is a common motivation for questions in this forum. My recommendation would be to avoid default values altogether (I'm specifically referring to the : syntax for arguments, not the standard mechanism for options). They actually make your code harder to maintain than other options (which I'll discuss), because it is actually tricky to figure out what will happen when you decide to slightly change things. It can also be confusing to figure out what will happen at all with default values. There are several reasonable strategies, and of course Mathematica can implement only one set of rules, and many folks find that the choice Mathematica makes does not match their intuition. Finally, there are known "gotchas" both in terms of performance problems and flat out defects.

Let's consider a simple, motivating example (simplified version of what you started with):

myFuncB[a_ : 1, b_ : "X"] := {a, b}

This seems to give you the convenience you want:

myFuncB[]
(* {1, "X"} *)

myFuncB[9]
(* {9, "X"} *)

But then we have:

myFuncB["Z"]
(* {"Z", "X"} *)

This is perfectly reasonable, but many people are surprised. Remember, Mathematica does not have a static type system. It does not assume that a should be an integer nor that b should be a string, even though the default values might suggest that to a human.

We can, of course fix this to a certain degree:

myFuncC[a_Integer : 1, b_String : "X"] := {a, b}

myFuncC["Z"]
(* {1, "Z"} *)

but this won't generalize to the case where your argument patterns aren't sufficiently distinct.

A strategy that provides more flexibility as well as greater control is to use polymorphic definitions:

myFuncD[a_Integer, b_String] := {a, b};
myFuncD[a_Integer] := myFuncD[a, "X"];
myFuncD[b_String] := myFuncD[1, b];
myFuncD[b_String, a_Integer] := myFuncD[a, b];
myFuncD[] := myFuncD[1, "X"];
myFuncD[{args___}] := myFuncD[args]

I've been exhaustive in this example for illustration purposes--you typically wouldn't need every combination. But, what this gives you is akin to type checking, both in terms of cardinality of the arguments as well as individual argument "types". You could layer some Condition expressions onto this for even more control. No matter how complicated this gets, it's still easier to maintain and understand than default values (this becomes clear quickly with just a couple of defaults).

Now, if you're in a situation where it just seems incredibly obvious that you need a default value, then you should consider either using Options (and the related option-handling functions) or SubValues. I won't go into how to use them or when to prefer one over the other. I bring them up just to illustrate that there are many alternatives to the explicit default-argument-value syntax.

There is a built in Default function. I haven't used it, but it might be better than inline defaults in the argument list. I can't imagine it's better than my previous suggestions though.

Finally, if you absolutely must have a default value, limit it to just one argument and make it the last argument.

POSTED BY: Eric Rimbey
Posted 2 years ago

An example:

In[1]:= f[a_ : 50, b_ : 60, OptionsPattern[{c -> 70}]] := {a, b, OptionValue[c]}

In[2]:= f[10, 20]
Out[2]= {10, 20, 70}

In[3]:= f[10]
Out[3]= {10, 60, 70}

In[4]:= f[]
Out[4]= {50, 60, 70}

In[5]:= f[10, 20, c -> 3]
Out[5]= {10, 20, 3}
POSTED BY: Hans Milton
Posted 2 years ago

What version of Mathematica are you using? I don't get this behavior with 13.1. Nevertheless, you have at least a couple of options.

First, you could make your pattern more specific:

myFuncA[a_ : 1, b_ : 2, opts___Rule] := {a, b, c /. {opts}}

Now 20 won't match opts.

Second, you could use the "standard" options handling mechanisms. Look in the documentation for OptionsPattern, OptionValue, and Options.

POSTED BY: Eric Rimbey
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard