How can I avoid using goto for a series of prompts? - goto

I'm writing a console based application that prompts a user for a series of questions. E.g:
"Enter a record to open:"
"Do you want to do X?"
"Do you want to do Y?"
"Are you sure you want to continue?"
If the user enters nothing at any prompt I want to go up one level. This is easy using goto. The only other way I can think of to do it is nested for loops which looks far uglier and gets pretty unwieldy for more than a couple of prompts. There must be an easy way to do this though, I just can't think of it.

You're basically writing a very simple state machine - use functions to represent every state: (I'll use random pseudocode, since you didn't specify a language)
get_information():
return get_record()
ask_record():
record = read_line()
return () if !record
return ask_y(record)
ask_x(record):
x = read_line()
return ask_record() if !x
return ask_y(record, x)
ask_y(record, x):
y = read_line()
return ask_x(record) if !y
return ask_continue(record, x, y)
ask_continue(record, x, y)
continue = read_line()
return ask_y(record, x) if !continue
return (record, x, y)
this is a trivial approach. In some languages the call stack will grow, in others it won't. If you have a language which causes the stack to grow, you can use trampolines to prevent it, by rewriting get_information to do:
x = get_information
while x is function:
x=x()
return x
ask_x(record):
x = read_line()
return (lambda: ask_record()) if !x
return (lambda: ask_y(record, x))
Or even abstracting the question and memory address of result in some question structure:
struct question {
question *next, *prev;
char prompt[];
char **result;
}
and then running in a loop, calling question* run_question(question*), going to ->next, or ->prev depending on the answer, until the result is NULL (as a stop condition, when results are filled in and no questions are left).
The last solution is probably the most "normal one" if you use an imperative language with direct pointer access.

Write it recursively instead of iteratively.

write it as a simple state machine.
while(running)
{
if (state == INIT)
{
out("enter record");
state = DO_X;
}
else if (state == DO_X)
{
do whatever for x.
state = WHATEVER_NEXT_STATE_IS;
}
}

Related

In functional programming, is "saving the state" of an algorithm at the recursive function argument cheating?

for instance, lets suppose we had to write an algorithm to get the max value of an array of integers, could we still call the code functional if we make the recursive function return various information that simulates an assignment to a global object? an exemple:
function getMax(array, props={}) {
const {index = 0, actualMax = array[0]}= props ///initial props
const arrayNotEnded = array[index + 1] !== undefined
if (arrayNotEnded) {
const maxOf= (a, b) => a > b ? a : b
const newMax = maxOf(actualMax, array[index+1])
const nextIndex = index+1
return getMax(array, {index:nextIndex, actualMax:newMax} )
}else return actualMax
}
a funny thing about that is, in Haskell, we cannot have optional arguments, so this logic would not be something cool to work with, since we would have to pass the initial props every time we would need to call this function.
Yes, you could consider it cheating, but this is a well-known technique in functional programming, the accumulator argument [1][2][3]. Remember: code doesn't become functional by not having state, functional programming is all about making state explicit. There's no better way of doing that than by making it a parameter of your function.
Your code has some other problems, though. Most prominently, the state should be internal to your function, only being passed to a helper function (that might be locally declared or separate) but not as part of your function's public interface. This also prevents confusing your helper function by passing invalid state (e.g. out-of-bound indices). And yes, also the optional parameter smells - not because you think this is not possible in Haskell (it is, using Maybe), but because it can be forgotten or passed mistakenly. Instead, the helper function should have a required state parameter, and getMax should have none.
Last but not least, you should avoid out-of-bounds indexed access on arrays - check the length to know where the end is, don't compare to undefined. This includes unconditionally accessing array[0] - that makes it very easy to overlook that your function can return undefined. Make this error condition explicit as well.
Here's how I'd write it:
function getMax(array) {
if (!array.length)
throw new Error("array must be non-empty");
else
return maxFrom(1, array[0]);
function maxFrom(index, max) {
if (index < array.length)
return maxFrom(index+1, array[index] > max ? array[index] : max);
else
return actualMax
}
}
Even better than throwing exceptions would be if you'd had an algebraic data type at hand that you could return to represent the error-or-result.

How to "leap of faith" when using recursion?

For me when making a recursive method. I always need to spend a lot of time to do it, because I will make some test cases and to see whether my recursive case works and to draw a stack diagram. However, when I ask other about it, they just say that I need to believe myself it will work. How am I suppose to believe that if you don't know what is going on in the recursive case?
You define what is going on in the recursive case, just as you define the rest of the method. Imagine someone else wrote a method to do what the one you are writing does; you wouldn't have a problem calling that, would you? The only difference is that you are that method's author, and it just happens to be the one being written.
For example: I am writing the following method:
// Sort array a[i..j-1] in ascending order
method sort_array( a, i, j ) {
..
}
The base case is easy:
if ( i >= j-1 ) // there is at most one element to be sorted
return; // a[i..j-1] is already sorted
Now, when that isn't true, I could do the following:
else {
k = index_of_max( a, i, j );
swap( a, j-1, k );
At this point, I know that a[j-1] has the correct value, so I just need to sort what comes before it -- fortunately, I have a method to do just that:
sort_array( a, i, j-1 );
}
No leap of faith is required; I know that recursive call will work because I wrote the method to do just that.

How do I convert a function into point free form?

Let's say I have a JavaScript function
function f(x) {
return a(b(x), c(x));
}
How would I convert that into a point free function? through composing functions? Also are there resources for more info on this?
In general, there's no easy rule to follow when you turn functions into point free style. Either you are going to have to guess, or you can just automate it. In the Haskell IRC channel, we have the lambdabot which is great at turning Haskell functions into point-free style. I usually just consult that, and then work my way backwards if I need to know how it works.
Your particular example can be solved using a couple of helpful functions. I'll show you below how it works, but be aware that it might require a lot of playing around to understand. It also helps if you know really, really basic lambda calculus, because the JavaScript syntax tends to get in the way sometimes.
Anyway, here goes:
Basically, to do this properly, you need three functions: fmap(f, g), ap(f, g) and curry(f). When you have those, f(x) is easily defined as (and this looks much neater in e.g. Haskell)
f = ap(fmap(curry(a), b), c);
The interesting bit lies in defining those three functions.
curry
Normally when you define functions of multiple arguments in JavaScript, you define them like
function f(x, y) {
// body
}
and you call them by doing something like f(3, 4). This is what is called an "uncurried function" in functional programming. You could also imagine defining functions like
function f(x) {
return function(y) {
//body
}
}
These functions are called "curried functions." (By the way, they are named after a mathematician whose name was Curry, if you wonder about the strange name.) Curried functions are instead called by doing
f(3)(4)
but other than that, the two functions behave very similarly. One difference is that it is easier to work with a point-free style when the functions are curried. Our curry function simply takes an uncurried function like the first one and turns it into a curried function like the second one. curry can be defined as
function curry(f) {
return function(a) {
return function(b) {
return f(a, b);
}
}
}
Now, you can use this. Instead of doing pow(3, 4) to get 81, you can do
cpow = curry(pow);
cpow(3)(4);
cpow is the curried version of pow. It doesn't take both arguments at the same time -- it takes them separately. In your specific case, this allows us to go from
function f(x) {
return a(b(x), c(x));
}
to
function f(x) {
return curry(a)(b(x))(c(x));
}
This is progress! (Although I admit it looks very weird in JavaScript...) Now, on to less spicy pastures.
fmap
The second piece of the puzzle is fmap(f, g), which takes two functions as arguments and composes them. What I'm saying is,
fmap(f, g)(x) == f(g(x))
This is easy to define, we just let
function fmap(f, g) {
return function(x) {
return f(g(x));
}
}
This is useful when you want to do two things in sequence. Say you want to do the useless operation log(exp(x)). You could do this the traditional way:
function logexp(x) {
return log(exp(x));
}
You could instead just do
logexp = fmap(log, exp);
This is commonly called composing two functions. To connect this to your example, last we left it off, we had refactored it into
function f(x) {
return curry(a)(b(x))(c(x));
}
We now notice some visual similarity between this and the function body of fmap. Let's rewrite this with fmap and it becomes
function f(x) {
return fmap(curry(a), b)(x)(c(x));
}
(to see how I got there, imagine that f = curry(a) and g = b. The last bit with c(x) isn't changed.)
ap
Our last puzzle piece is ap(f, g), which takes two functions and an argument, and does a weird thing. I won't even try to explain it, so I'll just show you what it does:
ap(f, g)(x) == f(x)(g(x))
Remember that f is really just a function of two arguments, only we write it a little differently to be able to do magic. ap is defined in JavaScript as
function ap(f, g) {
return function(x) {
return f(x)(g(x));
}
}
So, to put this in a more practical context: Say you want to raise a number to the square root of itself. You could do
function powsqrt(x) {
return pow(x, sqrt(x));
}
or, with your newfound knowledge of ap and remembering cpow from the first part about currying, you could also do
powsqrt = ap(cpow, sqrt);
This works because cpow is the curried version of pow. You can verify for yourself that this becomes the right thing when the definition of ap is expanded.
Now, to tie all this together with your example, we need to turn
function f(x) {
return fmap(curry(a), b)(x)(c(x));
}
Into the final, completely point-free version. If we look at the definition of ap, we see we can do something here to turn this into the point-free version!
function f(x) {
return ap(fmap(curry(a), b), c)(x);
}
Basically, the easiest way to understand this is to now "unfold" the call to ap. Replace the call to ap with the function body! What we get then, by merely substituting, is
function f(x) {
return function(y) {
return fmap(curry(a), b)(y)(c(y));
}(x);
}
I've renamed one x to y to avoid name collisions. This is still a bit weird, but we can make it a little shorter. After all, it is the same thing as
function f(x) {
return fmap(curry(a), b)(x)(c(x));
}
which was what we started with! Our call to ap was correct. If you want to, you can further unfold this to see that after everything is said and done, we actually end up with the very thing we started with. I leave that as an exercise.
Wrapping Up
Anyway, the last refactoring of your code made it into
function f(x) {
return ap(fmap(curry(a), b), c)(x);
}
which of course is the same thing as
f = ap(fmap(curry(a), b), c);
And that's it!

Explicitly calling return in a function or not

A while back I got rebuked by Simon Urbanek from the R core team (I believe) for recommending a user to explicitly calling return at the end of a function (his comment was deleted though):
foo = function() {
return(value)
}
instead he recommended:
foo = function() {
value
}
Probably in a situation like this it is required:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
His comment shed some light on why not calling return unless strictly needed is a good thing, but this was deleted.
My question is: Why is not calling return faster or better, and thus preferable?
Question was: Why is not (explicitly) calling return faster or better, and thus preferable?
There is no statement in R documentation making such an assumption.
The main page ?'function' says:
function( arglist ) expr
return(value)
Is it faster without calling return?
Both function() and return() are primitive functions and the function() itself returns last evaluated value even without including return() function.
Calling return() as .Primitive('return') with that last value as an argument will do the same job but needs one call more. So that this (often) unnecessary .Primitive('return') call can draw additional resources.
Simple measurement however shows that the resulting difference is very small and thus can not be the reason for not using explicit return. The following plot is created from data selected this way:
bench_nor2 <- function(x,repeats) { system.time(rep(
# without explicit return
(function(x) vector(length=x,mode="numeric"))(x)
,repeats)) }
bench_ret2 <- function(x,repeats) { system.time(rep(
# with explicit return
(function(x) return(vector(length=x,mode="numeric")))(x)
,repeats)) }
maxlen <- 1000
reps <- 10000
along <- seq(from=1,to=maxlen,by=5)
ret <- sapply(along,FUN=bench_ret2,repeats=reps)
nor <- sapply(along,FUN=bench_nor2,repeats=reps)
res <- data.frame(N=along,ELAPSED_RET=ret["elapsed",],ELAPSED_NOR=nor["elapsed",])
# res object is then visualized
# R version 2.15
The picture above may slightly difffer on your platform.
Based on measured data, the size of returned object is not causing any difference, the number of repeats (even if scaled up) makes just a very small difference, which in real word with real data and real algorithm could not be counted or make your script run faster.
Is it better without calling return?
Return is good tool for clearly designing "leaves" of code where the routine should end, jump out of the function and return value.
# here without calling .Primitive('return')
> (function() {10;20;30;40})()
[1] 40
# here with .Primitive('return')
> (function() {10;20;30;40;return(40)})()
[1] 40
# here return terminates flow
> (function() {10;20;return();30;40})()
NULL
> (function() {10;20;return(25);30;40})()
[1] 25
>
It depends on strategy and programming style of the programmer what style he use, he can use no return() as it is not required.
R core programmers uses both approaches ie. with and without explicit return() as it is possible to find in sources of 'base' functions.
Many times only return() is used (no argument) returning NULL in cases to conditially stop the function.
It is not clear if it is better or not as standard user or analyst using R can not see the real difference.
My opinion is that the question should be: Is there any danger in using explicit return coming from R implementation?
Or, maybe better, user writing function code should always ask: What is the effect in not using explicit return (or placing object to be returned as last leaf of code branch) in the function code?
If everyone agrees that
return is not necessary at the end of a function's body
not using return is marginally faster (according to #Alan's test, 4.3 microseconds versus 5.1)
should we all stop using return at the end of a function? I certainly won't, and I'd like to explain why. I hope to hear if other people share my opinion. And I apologize if it is not a straight answer to the OP, but more like a long subjective comment.
My main problem with not using return is that, as Paul pointed out, there are other places in a function's body where you may need it. And if you are forced to use return somewhere in the middle of your function, why not make all return statements explicit? I hate being inconsistent. Also I think the code reads better; one can scan the function and easily see all exit points and values.
Paul used this example:
foo = function() {
if(a) {
return(a)
} else {
return(b)
}
}
Unfortunately, one could point out that it can easily be rewritten as:
foo = function() {
if(a) {
output <- a
} else {
output <- b
}
output
}
The latter version even conforms with some programming coding standards that advocate one return statement per function. I think a better example could have been:
bar <- function() {
while (a) {
do_stuff
for (b) {
do_stuff
if (c) return(1)
for (d) {
do_stuff
if (e) return(2)
}
}
}
return(3)
}
This would be much harder to rewrite using a single return statement: it would need multiple breaks and an intricate system of boolean variables for propagating them. All this to say that the single return rule does not play well with R. So if you are going to need to use return in some places of your function's body, why not be consistent and use it everywhere?
I don't think the speed argument is a valid one. A 0.8 microsecond difference is nothing when you start looking at functions that actually do something. The last thing I can see is that it is less typing but hey, I'm not lazy.
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.
It seems that without return() it's faster...
library(rbenchmark)
x <- 1
foo <- function(value) {
return(value)
}
fuu <- function(value) {
value
}
benchmark(foo(x),fuu(x),replications=1e7)
test replications elapsed relative user.self sys.self user.child sys.child
1 foo(x) 10000000 51.36 1.185322 51.11 0.11 0 0
2 fuu(x) 10000000 43.33 1.000000 42.97 0.05 0 0
____EDIT __________________
I proceed to others benchmark (benchmark(fuu(x),foo(x),replications=1e7)) and the result is reversed... I'll try on a server.
My question is: Why is not calling return faster
It’s faster because return is a (primitive) function in R, which means that using it in code incurs the cost of a function call. Compare this to most other programming languages, where return is a keyword, but not a function call: it doesn’t translate to any runtime code execution.
That said, calling a primitive function in this way is pretty fast in R, and calling return incurs a minuscule overhead. This isn’t the argument for omitting return.
or better, and thus preferable?
Because there’s no reason to use it.
Because it’s redundant, and it doesn’t add useful redundancy.
To be clear: redundancy can sometimes be useful. But most redundancy isn’t of this kind. Instead, it’s of the kind that adds visual clutter without adding information: it’s the programming equivalent of a filler word or chartjunk).
Consider the following example of an explanatory comment, which is universally recognised as bad redundancy because the comment merely paraphrases what the code already expresses:
# Add one to the result
result = x + 1
Using return in R falls in the same category, because R is a functional programming language, and in R every function call has a value. This is a fundamental property of R. And once you see R code from the perspective that every expression (including every function call) has a value, the question then becomes: “why should I use return?” There needs to be a positive reason, since the default is not to use it.
One such positive reason is to signal early exit from a function, say in a guard clause:
f = function (a, b) {
if (! precondition(a)) return() # same as `return(NULL)`!
calculation(b)
}
This is a valid, non-redundant use of return. However, such guard clauses are rare in R compared to other languages, and since every expression has a value, a regular if does not require return:
sign = function (num) {
if (num > 0) {
1
} else if (num < 0) {
-1
} else {
0
}
}
We can even rewrite f like this:
f = function (a, b) {
if (precondition(a)) calculation(b)
}
… where if (cond) expr is the same as if (cond) expr else NULL.
Finally, I’d like to forestall three common objections:
Some people argue that using return adds clarity, because it signals “this function returns a value”. But as explained above, every function returns something in R. Thinking of return as a marker of returning a value isn’t just redundant, it’s actively misleading.
Relatedly, the Zen of Python has a marvellous guideline that should always be followed:
Explicit is better than implicit.
How does dropping redundant return not violate this? Because the return value of a function in a functional language is always explicit: it’s its last expression. This is again the same argument about explicitness vs redundancy.
In fact, if you want explicitness, use it to highlight the exception to the rule: mark functions that don’t return a meaningful value, which are only called for their side-effects (such as cat). Except R has a better marker than return for this case: invisible. For instance, I would write
save_results = function (results, file) {
# … code that writes the results to a file …
invisible()
}
But what about long functions? Won’t it be easy to lose track of what is being returned?
Two answers: first, not really. The rule is clear: the last expression of a function is its value. There’s nothing to keep track of.
But more importantly, the problem in long functions isn’t the lack of explicit return markers. It’s the length of the function. Long functions almost (?) always violate the single responsibility principle and even when they don’t they will benefit from being broken apart for readability.
A problem with not putting 'return' explicitly at the end is that if one adds additional statements at the end of the method, suddenly the return value is wrong:
foo <- function() {
dosomething()
}
This returns the value of dosomething().
Now we come along the next day and add a new line:
foo <- function() {
dosomething()
dosomething2()
}
We wanted our code to return the value of dosomething(), but instead it no longer does.
With an explicit return, this becomes really obvious:
foo <- function() {
return( dosomething() )
dosomething2()
}
We can see that there is something strange about this code, and fix it:
foo <- function() {
dosomething2()
return( dosomething() )
}
I think of return as a trick. As a general rule, the value of the last expression evaluated in a function becomes the function's value -- and this general pattern is found in many places. All of the following evaluate to 3:
local({
1
2
3
})
eval(expression({
1
2
3
}))
(function() {
1
2
3
})()
What return does is not really returning a value (this is done with or without it) but "breaking out" of the function in an irregular way. In that sense, it is the closest equivalent of GOTO statement in R (there are also break and next). I use return very rarely and never at the end of a function.
if(a) {
return(a)
} else {
return(b)
}
... this can be rewritten as if(a) a else b which is much better readable and less curly-bracketish. No need for return at all here. My prototypical case of use of "return" would be something like ...
ugly <- function(species, x, y){
if(length(species)>1) stop("First argument is too long.")
if(species=="Mickey Mouse") return("You're kidding!")
### do some calculations
if(grepl("mouse", species)) {
## do some more calculations
if(species=="Dormouse") return(paste0("You're sleeping until", x+y))
## do some more calculations
return(paste0("You're a mouse and will be eating for ", x^y, " more minutes."))
}
## some more ugly conditions
# ...
### finally
return("The end")
}
Generally, the need for many return's suggests that the problem is either ugly or badly structured.
[EDIT]
return doesn't really need a function to work: you can use it to break out of a set of expressions to be evaluated.
getout <- TRUE
# if getout==TRUE then the value of EXP, LOC, and FUN will be "OUTTA HERE"
# .... if getout==FALSE then it will be `3` for all these variables
EXP <- eval(expression({
1
2
if(getout) return("OUTTA HERE")
3
}))
LOC <- local({
1
2
if(getout) return("OUTTA HERE")
3
})
FUN <- (function(){
1
2
if(getout) return("OUTTA HERE")
3
})()
identical(EXP,LOC)
identical(EXP,FUN)
The argument of redundancy has come up a lot here. In my opinion that is not reason enough to omit return().
Redundancy is not automatically a bad thing. When used strategically, redundancy makes code clearer and more maintenable.
Consider this example: Function parameters often have default values. So specifying a value that is the same as the default is redundant. Except it makes obvious the behaviour I expect. No need to pull up the function manpage to remind myself what the defaults are. And no worry about a future version of the function changing its defaults.
With a negligible performance penalty for calling return() (as per the benchmarks posted here by others) it comes down to style rather than right and wrong. For something to be "wrong", there needs to be a clear disadvantage, and nobody here has demonstrated satisfactorily that including or omitting return() has a consistent disadvantage. It seems very case-specific and user-specific.
So here is where I stand on this.
function(){
#do stuff
...
abcd
}
I am uncomfortable with "orphan" variables like in the example above. Was abcd going to be part of a statement I didn't finish writing? Is it a remnant of a splice/edit in my code and needs to be deleted? Did I accidentally paste/move something from somewhere else?
function(){
#do stuff
...
return(abdc)
}
By contrast, this second example makes it obvious to me that it is an intended return value, rather than some accident or incomplete code. For me this redundancy is absolutely not useless.
Of course, once the function is finished and working I could remove the return. But removing it is in itself a redundant extra step, and in my view more useless than including return() in the first place.
All that said, I do not use return() in short unnamed one-liner functions. There it makes up a large fraction of the function's code and therefore mostly causes visual clutter that makes code less legible. But for larger formally defined and named functions, I use it and will likely continue to so.
return can increase code readability:
foo <- function() {
if (a) return(a)
b
}

Base case in a recursive method

A theoretical question here about the base or halting case in a recursive method, what's its standards?
I mean, is it normal not to have body in it, just a return statement?
Is it always like the following:
if (input operation value)
return sth;
Do you have different thoughts about it?
The pattern for recursive functions is that they look something like this:
f( value )
if ( test value )
return value
else
return f( simplify value )
I don't think you can say much more than that about general cases.
The base case is to terminate the loop (avoid becoming an infinite recursion). There's no standard in the base case, any input that is simple enough to be solved exactly can be chosen as one.
For example, this is perfectly valid:
int factorial (int n) {
if (n <= 5) {
// Not just a return statement
int x = 1;
while (n > 0) {
x *= n;
-- n;
}
return x;
} else {
return n * factorial(n-1);
}
}
In some cases, your base case is
return literal
In some cases, your base case is not simply "return a literal".
There cannot be a "standard" -- it depends on your function.
The "Syracuse Function" http://en.wikipedia.org/wiki/Collatz_conjecture for example,
doesn't have a trivial base case or a trivial literal value.
"Do you have different thoughts about it??" Isn't really a sensible question.
The recursion has to terminate, that's all. A trivial tail recursion may have a "base case" that returns a literal, or it may be a calculation. A more complex recursion may not have a trivial "base case".
It depends entirely on the particular recursive function. In general, it can't be an empty return; statement, though - for any recursive function that returns a value, the base case should also return a value of that type, since func(base) is also a perfectly valid call. For example, a recursive factorial function would return a 1 as the base value.

Resources