Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

Perhaps this is poor programming on my part, but I would like to create a function that uses a variable as an optional parameter:

var = 2;
f[a_:var] := {a}

The desired output would be:

f[]
(* {2} *)
var = 5;
f[]
(* {5} *)

However the optional a maintains the value of var when f was defined:

var = 2;
f[a_:var] := {a}
var = 5;
f[]
(* {2} *)
f[5]
(* {5} *)

I can work around this somewhat by making the optional value dynamic:

var = 2;
f[a_:Dynamic[var]] := {a}
f[];
(* {2} *)
var = 5;
(* Above output updates to 5 *)

I can imagine having some fun with this type of behavior, but it is not what I want at the moment. Is it possible to set an optional parameter that can be updated? I'll also happily accept an answer that tells me why this would be a bad idea.

share|improve this question
    
It is not what you are asking for but it does the same thing: f[x_]:={x}; f[]:={a}; –  Kuba Jul 22 '13 at 20:44
1  
All you have to do is to define your function before var has any value, or use Block, such as Block[{var}, f[a_:var]:={a}]. See also this answer and a discussion in comments below it. –  Leonid Shifrin Jul 22 '13 at 20:46
1  
Related FYI: Default appears to allow variable values, but it does not. You may already know this, but it's tripped up quite a few users I believe. Also, I see that Simon (I miss his participation) posted effectively the same method that Leonid just showed in answer to that old question of mine. –  Mr.Wizard Jul 23 '13 at 8:54

1 Answer 1

up vote 7 down vote accepted

This is possible to do, in at least two ways. The first method is to make sure that your function's definition is entered first, so that the default variable var has no value yet:

ClearAll[var,f];
f[a_: var] := {a};
var = 2;

f[]

(* {2}  *)

var = 5;
f[]

(* {5}  *)

The second method would work regardless of whether or not the variable var has currently a value:

ClearAll[var, f];
var = 2;
Block[{var}, f[a_: var] := {a}]

and then again:

f[]

(* {2}  *)

var = 5;
f[]

(* {5}  *)

The reason this works is that what matters is only what var evaluates to at the moment of definition. It is then being evaluated by SetDelayed, and its value is what is being used to form the resulting global rule. Once that global rule (definition) has been added to the global rule base, it does not matter any more what happens to var after that.

A closely related discussion can be found here. A question of whether or not to actually use such constructs is a matter of taste. I don't see a good reason to not recommend ever using it. If you use the Block-based method, then it should be reasonably safe. Usual warnings related to the use of global variables do apply, but I do acknowledge that there may be cases where such construct may be justified.

share|improve this answer
    
Thanks - your discussion makes perfect sense, and makes me think I should have figured this one out myself! –  bobthechemist Jul 22 '13 at 21:02
1  
@bobthechemist Glad I could help, and thanks for the accept. I think this was the fastest accept for any of my answers - within 3 minutes of posting the answer :). You could have waited for some time with accepting though, to encourage others to contribute more answers. As for the subject matter, this actually seems to be one of these issues which look very simple once we know the answer, but actually are not so simple. I seem to remember that it took me some time to figure this out when I asked myself the same question, back when I didn't know the answer. –  Leonid Shifrin Jul 22 '13 at 21:04
    
I was been playing with and being frustrated by this just last week. You're right, obvious, once you know the answer. –  rcollyer Jul 22 '13 at 21:18
    
@rcollyer Well, sorry to hear that. The answer was also on SO, but my version was only in comments to Brett's answer, and one has to remember which question was that. In fact, since it was in comments, it took me some time to find that discussion now. I only managed to do find it because I remembered who posted one of the answers to that question (Brett). –  Leonid Shifrin Jul 22 '13 at 21:20
    
@LeonidShifrin I do typically try to hold off accepting answers so that others may comment, but yours hit the nail right on the head so I figured I would accept and move on. –  bobthechemist Jul 22 '13 at 21:40

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.