8
$\begingroup$

Is there a neat way to define a function with a single optional argument that defaults to an empty sequence? For example, suppose I want to define a wrapper for RandomReal that allows zero or one arguments:

myRandom[] := RandomReal[];
myRandom[x_] := RandomReal[x];

I can see two more compact ways of doing this. The first is to allow any number of arguments and then check whether we have zero or one:

myRandom[x___] := RandomReal[x] /; Length[{x}] ≤ 1;

and the other is to use a default argument of Sequence[], wrapped with Hold:

myRandom[x_:Hold[Sequence[]]] := ReleaseHold[RandomReal[x]];

But neither of these seems very neat, and might be problematic or inefficient in some (more realistic) cases. Is there a conventional and/or neat way of doing this sort of thing?

$\endgroup$
4
  • $\begingroup$ What about myRandom[x___] := RandomReal[x]? $\endgroup$
    – Yves Klett
    May 14, 2014 at 9:45
  • 1
    $\begingroup$ @YvesKlett OP doesn't want myRandom[1, 2, 3] to pass through. $\endgroup$
    – Kuba
    May 14, 2014 at 9:46
  • $\begingroup$ @Kuba Thanks for the tip! I asked for a neat way of doing it, and you provided one. But I agree that OleksandrR.'s is neater. $\endgroup$ May 14, 2014 at 11:22
  • $\begingroup$ Related: (29208) $\endgroup$
    – Mr.Wizard
    May 14, 2014 at 15:09

3 Answers 3

8
$\begingroup$

At Kuba's request:

The other answer is a reasonable solution if we take the question at face value and assume that the default value of Sequence[] is strictly required. Of course, it might (often) be the case in practice that we would rather have nothing at all, in place of an empty sequence, or that we would prefer not have to use a Hold* attribute. Some functions we might call could have SequenceHold, for example, and may not respond well to receiving Sequence[] as an argument.

An alternative is to use a pattern containing, well, Alternatives. For instance:

ClearAll[myRandom];
myRandom[x_ | PatternSequence[]] := RandomReal[x];

Trace[myRandom[]]
(* -> {myRandom[], RandomReal[], 0.492105} *)

Trace[myRandom[5]]
(* -> {myRandom[5], RandomReal[5], 1.52175} *)

As we see, Sequence[] is not introduced anywhere in the process.

We can also use this to impose restrictions on acceptable values for x (e.g., x_Integer, or x : {__Integer}) for which Sequence[] might not be a match. For an application, see this recent answer.

Footnote

The documentation is not clear on which of x_ | PatternSequence[] or x : _ | PatternSequence[] is more correct (if either is) with respect to the ultimate value assigned to x by the pattern matcher. Functionally, they are equivalent. But, in principle, any value might be assigned to a pattern that doesn't match, so there could be a theoretical argument to prefer the latter form.

$\endgroup$
6
$\begingroup$

There are several different ways to produce this behavior.

Repeated

Perhaps the most direct way is to use Repeated:

foo[x : Repeated[_, {0, 1}]] := bar[x]

foo[]
foo[1]
foo[1, 2]
bar[]
bar[1]
foo[1, 2]

PatternSequence

Oleksandr already posted one method that I like:

ClearAll[foo]

foo[x_ | PatternSequence[]] := bar[x]

The pattern PatternSequence[] will match a non-existent argument. Key here is that if x_ does not match (and Alternatives moves on to the next attempt) x vanishes from the right-hand side; or if you prefer it behaves (for the most part) as if it is replaced with Sequence[]. See this answer of mine for more examples of and uses for this behavior. To further illuminate this method note that this also works:

ClearAll[foo]

foo[x_ | Repeated[_, {0}]] := bar[x]

Also, if you want (or would allow) multiple arguments to foo to return as bar[] you could use:

ClearAll[foo]

foo[x_ | ___] := bar[x]

foo[]
foo[1]
foo[1, 2]
bar[]

bar[1]

bar[]

Default

For the example you gave perhaps the most canonical is to use Optional and Default (as Kuba showed), which works because Set has the SequenceHold attribute. This also results in a clean primary definition:

ClearAll[foo]

Default[foo] = Sequence[];

foo[x_.] := bar[x]

Note that Sequence[] remains unevaluated in the Default definition:

?foo
Global`foo

foo[x_.] := bar[x]

Default[foo] = Sequence[]

See A question about two ways to use Default to better understand Default.

$\endgroup$
3
$\begingroup$

You may use HoldFirst Attribute:

ClearAll[myRandom]
SetAttributes[myRandom, HoldFirst]; 
myRandom[x_: Sequence[]] := RandomReal[x];

Another way with Default:

ClearAll[myRandom]
Default[myRandom] = Sequence[];
myRandom[x_.] := RandomReal[x];
$\endgroup$
2
  • 3
    $\begingroup$ I think this is a reasonable solution if we take the question at face value and assume that the default value of Sequence[] is strictly required. Of course, it might be the case in practice that we would rather have nothing at all, in place of an empty sequence, or that we would rather not have the Hold* attribute. In that case one can use x_ | PatternSequence[] as the pattern. $\endgroup$ May 14, 2014 at 10:37
  • $\begingroup$ @OleksandrR. I think you should post this as an answer. I don't know why I always forget the PatternSequence. Imo it is the best solution. $\endgroup$
    – Kuba
    May 14, 2014 at 10:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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