This is an interesting discussion. I think that @flodel's example is excellent. However, I think it illustrates my point (and @koshke mentions this in a comment) that return
makes sense when you use an imperative instead of a functional coding style.
Not to belabour the point, but I would have rewritten foo
like this:
foo = function() ifelse(a,a,b)
A functional style avoids state changes, like storing the value of output
. In this style, return
is out of place; foo
looks more like a mathematical function.
I agree with @flodel: using an intricate system of boolean variables in bar
would be less clear, and pointless when you have return
. What makes bar
so amenable to return
statements is that it is written in an imperative style. Indeed, the boolean variables represent the "state" changes avoided in a functional style.
It is really difficult to rewrite bar
in functional style, because it is just pseudocode, but the idea is something like this:
e_func <- function() do_stuff
d_func <- function() ifelse(any(sapply(seq(d),e_func)),2,3)
b_func <- function() {
do_stuff
ifelse(c,1,sapply(seq(b),d_func))
}
bar <- function () {
do_stuff
sapply(seq(a),b_func) # Not exactly correct, but illustrates the idea.
}
The while
loop would be the most difficult to rewrite, because it is controlled by state changes to a
.
The speed loss caused by a call to return
is negligible, but the efficiency gained by avoiding return
and rewriting in a functional style is often enormous. Telling new users to stop using return
probably won't help, but guiding them to a functional style will payoff.
@Paul return
is necessary in imperative style because you often want to exit the function at different points in a loop. A functional style doesn't use loops, and therefore doesn't need return
. In a purely functional style, the final call is almost always the desired return value.
In Python, functions require a return
statement. However, if you programmed your function in a functional style, you will likely have only one return
statement: at the end of your function.
Using an example from another StackOverflow post, let us say we wanted a function that returned TRUE
if all the values in a given x
had an odd length. We could use two styles:
# Procedural / Imperative
allOdd = function(x) {
for (i in x) if (length(i) %% 2 == 0) return (FALSE)
return (TRUE)
}
# Functional
allOdd = function(x)
all(length(x) %% 2 == 1)
In a functional style, the value to be returned naturally falls at the ends of the function. Again, it looks more like a mathematical function.
@GSee The warnings outlined in ?ifelse
are definitely interesting, but I don't think they are trying to dissuade use of the function. In fact, ifelse
has the advantage of automatically vectorizing functions. For example, consider a slightly modified version of foo
:
foo = function(a) { # Note that it now has an argument
if(a) {
return(a)
} else {
return(b)
}
}
This function works fine when length(a)
is 1. But if you rewrote foo
with an ifelse
foo = function (a) ifelse(a,a,b)
Now foo
works on any length of a
. In fact, it would even work when a
is a matrix. Returning a value the same shape as test
is a feature that helps with vectorization, not a problem.