3
$\begingroup$

I am trying to make a function with an optional argument that may change but mathematica caches the value when the function is defined.

Here is an example where the optional time variable is set when the function is defined.

Remove[CurrentTime]
CurrentTime[time_: DateList[]] := {time, DateList[]};
Pause[1]
CurrentTime[]

The output is split by 1 second

{{2013, 6, 4, 13, 57, 46.149504}, {2013, 6, 4, 13, 57, 47.150726}}

Is there a way to tell it its a delayed set, or to just read the function each time?

$\endgroup$
  • $\begingroup$ ...what sort of behavior were you expecting? $\endgroup$ – J. M. will be back soon Jun 4 '13 at 4:34
  • $\begingroup$ I want the time argument to default to the current time in this case, ie, they should show the same time. $\endgroup$ – Richard Jun 4 '13 at 4:40
  • $\begingroup$ Well, the problem is that you're effectively evaluating {DateList[], DateList[]}; it stands to reason that some amount of time should have to pass in between the two times you call DateList[]... $\endgroup$ – J. M. will be back soon Jun 4 '13 at 4:45
  • $\begingroup$ That is by design to illustrate the problem, there is a pause of 1 second to show that the default value for time is set once and never changed. I would like to make a function in which you can pass in the time, but if you dont it defaults to the current time. $\endgroup$ – Richard Jun 4 '13 at 4:49
  • $\begingroup$ Well, you might be interested in CurrentTime[OptionsPattern[{"Time" :> DateList[]}]] := {OptionValue["Time"], DateList[]}. One could either do CurrentTime[] or CurrentTime["Time" -> {2013, 6, 4, 12, 0, 0}]... $\endgroup$ – J. M. will be back soon Jun 4 '13 at 4:52
6
$\begingroup$
CurrentTime[time_: Hold[DateList[]]] := {ReleaseHold@time, DateList[]};
Pause[1]
CurrentTime[]

{{2013, 6, 4, 8, 29, 34.8437500}, {2013, 6, 4, 8, 29, 34.8437500}}

Edit

This works too:

Remove[CurrentTime]
SetAttributes[CurrentTime, HoldAll]
CurrentTime[time_: DateList[]] := {time, DateList[]};
$\endgroup$
  • $\begingroup$ Ah, it was the Attributes[]... I do think your second solution is much better than the first. $\endgroup$ – J. M. will be back soon Jun 4 '13 at 6:42
  • $\begingroup$ @0x4A4D It is more elegant but if, for example, You have 3 arguments with Hold and 4 without and only HoldAll, HoldRest, HoldFirst Attributes avalible then the first is useful. $\endgroup$ – Kuba Jun 4 '13 at 6:49
2
$\begingroup$

HoldPattern and Unevaluated work too. You can also remove the HoldAll once you made the definition if you like.

ClearAll[f, g, h]
f[HoldPattern@Optional[x_, RandomReal[]]] := x
g[Unevaluated@Optional[x_, RandomReal[]]] := x
h~SetAttributes~HoldAll
h[Optional[x_, RandomReal[]]] := x
h~SetAttributes~{}
h~ClearAttributes~HoldAll
{f[], f[]}
f[1]
{g[], g[]}
g[1]
{h[], h[]}
h[1]


{0.683758, 0.584808}

1

{0.793986, 0.0783635}

1

{0.518827, 0.806583}

1

You just need to get the function you want to give the default argument unevaluated into the replacement rule that is created: DownValues@g now gives

{HoldPattern[g[x_ : RandomReal[]]] :> x}

You could even do

Unprotect@Optional
SetAttributes[Optional, HoldRest]
ll[Optional[x_, RandomReal[]]] := x
ll[]
ll[1]
$\endgroup$
  • $\begingroup$ @Kuba Unfortunately, that does not work: Calling g[] then gives Unevaluated[RandomReal[]] $\endgroup$ – masterxilo Aug 18 '16 at 16:23
  • $\begingroup$ Agreed, I've realised this today morning but forgot to delete the comment :) $\endgroup$ – Kuba Aug 18 '16 at 16:25
1
$\begingroup$
HeldOptional = Function[, HoldPattern@Optional[#1, #2], HoldRest]

Remove[CurrentTime]
CurrentTime[time_~HeldOptional~DateList[]] := {time, DateList[]};
Pause[1]
CurrentTime[]

c.f. How to write a `HeldOptional` variant of `Optional` that does not evaluate its second argument?

$\endgroup$
  • $\begingroup$ Sure wish they'd put this in the Help. As of 11.2 there is no mention of such: ref/Optional. $\endgroup$ – berniethejet Jun 13 at 13:04
1
$\begingroup$

I would use Automatic as the default value, then do this:

currentTime[time_: Automatic] := Replace[time, Automatic -> DateList[]]

I used Replace, not ReplaceAll. Unlike ReplaceAll, Replace is safe because it only does the replacement when the whole expression matches. It won't replace subexpressions.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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