49
$\begingroup$

I'd like to define a function with several optional arguments, some of which default to the value supplied for other arguments. For example, I'd like to be able to write something like

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

and have

{f[1, 2], f[1]}

produce

{{1, 2, 2}, {1, 0, 0}}

Instead I get

{{1, 2, y}, {1, 0, y}}

Can I make a default for an optional argument the value of another argument? If not, what's the best approach for accomplishing this?

$\endgroup$
1
  • 1
    $\begingroup$ Are you an R user? I love this feature in R ... never seen it in any other language. $\endgroup$
    – Gabriel
    Dec 4, 2012 at 20:41

6 Answers 6

29
$\begingroup$

You can't easily do this with optional arguments (but see Leonid's answer for a work around), but you can use the fact that you can have multiple definitions for a given function:

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

will do what you want.

For further use of this style you could also do this as:

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

which makes the "pattern" of your function even more explicit

$\endgroup$
28
$\begingroup$

Yes you can, although this is not completely trivial:

Module[{yy},
  f[x_, y_: 0, z_: yy] := Block[{yy = y}, {x, y, z}]
]

What is happening here is that I set the default to a local variable, which I then dynamically set (via Block) to a second argument. So,

{f[1,2],f[1]}

(*  {{1,2,2},{1,0,0}}  *)
$\endgroup$
16
  • 3
    $\begingroup$ @Gabriel One must be careful with saying "x is impossible in Mathematica", I think most of us got into this trap at least once. Never say never :) $\endgroup$ Dec 4, 2012 at 21:20
  • 1
    $\begingroup$ You can dispense with the Module and simply write: g[x_, y_: 0, z_: \[FormalY]] := Block[{\[FormalY] = y}, {x, y, z}]. It makes for cleaner looking code in the FE :) $\endgroup$
    – rm -rf
    Dec 4, 2012 at 21:20
  • 2
    $\begingroup$ @rm-rf: You guys are scaring me. $\endgroup$
    – orome
    Dec 4, 2012 at 21:32
  • 2
    $\begingroup$ @rm-rf Besides, what makes you think that \[FormalY] has not been uprotected and assigned a value? :) $\endgroup$ Dec 4, 2012 at 21:33
  • 1
    $\begingroup$ @LeonidShifrin If we're going down that road, what makes you think Module has not been unprotected and redefined? :D Nevertheless, point taken. (I do agree with the readability in any editor part — a fact I began to appreciate only after using the WB) $\endgroup$
    – rm -rf
    Dec 4, 2012 at 21:44
11
$\begingroup$

A less elegant version than Gabriel's, and a less economic than Leonid's, using ReplaceAll:

f[x_, y_: 0, z_: Automatic] := {x, y, z /. Automatic -> y}
$\endgroup$
3
  • $\begingroup$ super cute :-) +1 $\endgroup$
    – Gabriel
    Dec 4, 2012 at 22:46
  • $\begingroup$ Very nice, +1. The reason I did not go this way is that you will have to hunt down all occurrences of z and replace them with z/.Automatic -> y, and if you forget some, there isn't any easy way to see that, so for functions with more complex / larger bodies this will be harder to maintain. But for a simple function, this is quite elegant. $\endgroup$ Dec 4, 2012 at 23:03
  • $\begingroup$ @Leonid yes, you are right, but I had the vision of Options in front of my eyes :) Of course that involves a Module and a local variable to replace z everywhere, as you have pointed out. $\endgroup$ Dec 5, 2012 at 20:36
10
$\begingroup$

Already late to the party, but here is another approach:

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

Another Optional trick is the following:

ClearAll[f]
f[x : (y_) : 1] := {x, y}

Here the colon is used twice. Once as shorthand for Pattern and once as shorthand for Optional. This is not appropriate for you question. I just wanted to mention it.

Edit 1:

Since optional arguments are all about pattern matching, here a list of possible patterns and allowed syntax:

InputForm  | FullForm
-----------|---------
x          |  x
_*x        |  Times[Blank[], x]
(_.)*x     |  Times[Optional[Blank[]], x]
_          |  Blank[]
x*_        |  Times[x, Blank[]]
_x         |  Blank[x]
x . _      |  Dot[x, Blank[]]
_ . x      |  Dot[Blank[], x]
_.         |  Optional[Blank[]]
x*(_.)     |  Times[x, Optional[Blank[]]]
x_.        |  Optional[Pattern[x, Blank[]]]
_:x        |  Optional[Blank[], x]
x_         |  Pattern[x, Blank[]]
x:(_.)     |  Pattern[x, Optional[Blank[]]]
x /. _     |  ReplaceAll[x, Blank[]]
x /. _.    |  ReplaceAll[x, Optional[Blank[]]]
_ /. x     |  ReplaceAll[Blank[], x]
_. /. x    |  ReplaceAll[Optional[Blank[]], x]

Edit 2:

Another alternative is the following:

Default[f] = def;
f[x_, y_.] := Block[{def = x}, {x, y}]

Probably this is the best form of all I've listed here.

$\endgroup$
6
$\begingroup$

I think a good way to handle this is to pass the arguments back to the function. This is very similar to what Gabriel already proposed, but when the RHS is more complex it will be cleaner. Unlike Leonid's method it does not rely on Symbol evaluation and will therefore work inside held constructs.

Example:

f[x_, y_: 0]  := f[x, y, y]
f[x_, y_, z_] := Hold[x, y, z]

f[5]

f[5, 7]
Hold[5, 0, 0]

Hold[5, 7, 7]

If the actual RHS were longer you can see why one would not wish rewrite it for the optional case:

f[x_, y_, z_] :=
  -x^3 - x^4 - x^3 y + 3 x^2 z + 3 x^3 z + 3 x^2 y z - 3 x z^2 - 3 x^2 z^2 - 
    3 x y z^2 + z^3 + x z^3 + y z^3
$\endgroup$
1
$\begingroup$

Here is another solution: (it's kind of cheating but it is very compact and works fine)

f[x_, y_: 0, z_:"something"] := If[z==="something", {x, y, y}, {x,y,z}]
$\endgroup$
2
  • 1
    $\begingroup$ Try out f[1, 2, a]. I'd expect to get {1, 2, a}. $\endgroup$
    – Michael E2
    Feb 26, 2021 at 2:55
  • $\begingroup$ @MichaelE2 yeah, good point. I just edited my answer and put an extra "=" in "z=="something" so that it works in this case too. $\endgroup$ Feb 27, 2021 at 22:16

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.