Mathematica Stack Exchange is a question and answer site for users of Mathematica. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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?

share|improve this question
1  
Are you an R user? I love this feature in R ... never seen it in any other language. – Gabriel Dec 4 '12 at 20:41
up vote 20 down vote accepted

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

share|improve this answer

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}}  *)
share|improve this answer
2  
@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 :) – Leonid Shifrin Dec 4 '12 at 21:20
1  
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 :) – R. M. Dec 4 '12 at 21:20
2  
@rm-rf: You guys are scaring me. – raxacoricofallapatorius Dec 4 '12 at 21:32
1  
@rm-rf Besides, what makes you think that \[FormalY] has not been uprotected and assigned a value? :) – Leonid Shifrin Dec 4 '12 at 21:33
1  
@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) – R. M. Dec 4 '12 at 21:44

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}
share|improve this answer
    
super cute :-) +1 – Gabriel Dec 4 '12 at 22:46
    
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. – Leonid Shifrin Dec 4 '12 at 23:03
    
@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. – István Zachar Dec 5 '12 at 20:36

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.

share|improve this answer

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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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