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.

In a comment to my answer to this question, QuantumDot asks, "Where can I find more information on when I must use the 'Return' function?". I found this too deep a question to be answered by leaving a comment in reply. I think it deserves to be asked as an question in own right.

I am posting my answer below, but I hope there will be others. I doubt I have the definitive view of this matter.

share|improve this question

2 Answers 2

up vote 6 down vote accepted

Return is surprisingly under-documented, but I would still recommend reading Return as a start. Return has an undocumented feature which you can read about here

But none of the above really answers the question. Frankly, I can't come up with a situation that absolutely requires the use of Return, although I can think of several where using Return is convenient.

That said, I will try to give a rule-of-thumb. Use Return to terminate the evaluation of a compound expression inside a function at any point before the final argument of the compound expression is evaluated. Does that read as gibberish? To make sense of it, recall that a; b; c is actually the short form of CompoundExpression[a, b, c], which returns whatever c evaluates to. To get a; b; c to stop after evaluating b and return, write a; Return[b]; c instead.

Of course, in normal usage Return will be placed in some kind of conditional construct. For example,

f[x_] := (a; If[x > 0, Return[a]]; b)
{f[42], f[-42]}
{a, b}

But this can be rewritten, as all the cases I can think of, to eliminate the use of Return.

g[x_] := If[x > 0, a, b]

So when is it practical to use Return in "real" code? I find myself using Return in the definitions of functions that have non-standard evaluation; e.g., functions that have the the HoldAll attribute. In such functions, there is a need to validate arguments that have been passed in. It is convenient to do this up front, using Return to cut short evaluation should a bad argument be found. Here is a rather contrived example.

Clear @ h
SetAttributes[h, HoldAll];
h::badarg = "`1` = `2` is not valid";
h[x_, y_] :=
  Module[{result = $default},
    If[! MatchQ[x, _Integer /; x > 0], Message[h::badarg, "x", x]; Return[$Failed]];
    If[! MatchQ[y, _Real], Message[h::badarg, "y", y ]; Return[$Failed]];
    (* do some long computation here *)
    result]

h[42, 42.]
$default
h[-42, 42.]

h::badarg: x = -42 is not valid

 $Failed
h[42, 42]

h::badarg: y = 42 is not valid

 $Failed

Another thing good to know about Return is that it is not a function in the ordinary Mathematica sense. It's a symbol which has special meaning to the Mathematica evaluator and which causes a side effect. If Return were an ordinary function a; Return[b]; c would be the same as a; b; c.

The non-functional behavior of Return is demonstrated by the following rather peculiar sequence of evaluations.

expr = CompoundExpression[a, Return[b], c];
Return[b]
OwnValues @ expr
{HoldPattern[expr] :> Return[b]}
expr
b
share|improve this answer

I think the most direct answer to this question is: rarely.

Incorrect Usage

A great majority of the appearances of Return that I see are either entirely superfluous or an indication of code in need of refactoring. For example I frequently see something like:

fn[x_] :=
  Module[{vec},
    vec = {1, 2, 3};
    Do[vec[[i]] *= x, {i, 1, Length[vec]}];
    Return[vec]
  ]

Return is superfluous here as omitting it will produce the same result. And of course the rest this code would be written far more simply which is often true in such cases.

Correct Usage

Return does have its place, as does its sister function Break. These functions are the most direct way to exit an ongoing incremental evaluation, and optionally return a value. It is important to understand how they work but there are already questions on that subject:

Although mostly undocumented I often, even typically, use these functions with additional parameters:

This not only extends the application of Return and Break but it more clearly defines the behavior of the operation which may make it more robust through version changes.

One must understand the order of evaluation and operation of Mathematica functions to effectively use Return and Break. For example it is not possible to Break[] out of a Map operation because Map first applies the function, then evaluates the expression, as explained here:

However Scan does use an incremental evaluation which means that we can Return or Break from it:

Scan[
  If[# > 2, Return[#]] &,
  {1, 2, 3, 4, 5}
]
3

Critically, though not illustrated, the Function is never applied to elements 4 and 5.

The question title is not when should I use Return but rather when must I use return and that is even more rare, if ever. The operation above can also be written using Throw and Catch:

Scan[
  If[# > 2, Throw[#]] &,
  {1, 2, 3, 4, 5}
] // Catch
3

(Like Return using the second parameter of Throw and Catch will make this operation more robust.)

Exclusive Usage

I am not prepared to say that there is ever a case where one must use Return as there are so often clever alternative methods in Mathematica. However one example where it is at least far easier to use Return is if it is not practical to insert a Catch at an arbitrary place in the evaluation.

Suppose we have a function with multiple internal operations which itself accepts a function:

func[f_, dat_] := Array[f, #, #2] & ~MapIndexed~ dat ~Flatten~ {2}

func[f, {4, 1, 3}]
{{f[1], f[2], f[3]}, {f[2], f[4]}, {f[3], f[5]}, {f[4]}}

Without modifying func we can use Return to affect the evaluation in different ways:

func[If[# > 4, Return[{0, 7, 9}, Array], #] &, {4, 1, 3}]

func[If[# > 4, Return[{{1, 2}, {3}}, MapIndexed], #] &, {4, 1, 3}]

func[If[# > 4, Return["foo", func], #] &, {4, 1, 3}]
{{1, 2, 0}, {2, 7}, {3, 9}, {4}}

{{1, 3}, {2}}

"foo"

I apologize for obfuscation from what was perhaps a poorly chosen example function, but the point is that we can exit from and return a value for an individual use of Array, or the one call to MapIndexed, or the entire function func.

Note that there was no example of Flatten for the parameter of Return because it does not work:

func[If[# > 4, Return["x", Flatten], #] &, {4, 1, 3}]

Return::nofunc: Function Flatten not found enclosing Return[x,Flatten]. >>

Hold[Return["x", Flatten]]

The reason for this is complicated and it is the subject of Rojo's answer found in the second bulleted link from the top of this post.

share|improve this answer
    
I gave a link to Rojo's answer in my answer as auxiliary reading. It's a great contribution and definitely related to this question, but in my opinion, not quite on-topic for the issue under consideration here. –  m_goldberg Aug 24 at 20:36
    
@m_goldberg That's why I also included a link, but only a link, in my answer. Although I hope that my answer is complementary to yours I also wanted to make it stand on its own so I thought it important to reference some guides to the rather mysterious behavior of Return. –  Mr.Wizard Aug 25 at 4:26
    
I feel your answer is entirely complementary to mine, and is exactly the kind of additional answer I was hoping would be posted. –  m_goldberg Aug 25 at 4:41
    
@m_goldberg Okay. :-) –  Mr.Wizard Aug 25 at 4:45

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.