For a function to be tail recursive, there must be nothing to do after the function returns except return its value. That is, the last thing that happens in the recursive step is the call to the function itself. This is generally achieved by using an accumulator parameter for keeping track of the answer:
(define (factorial x acc)
(if (zero? x)
acc
(factorial (sub1 x) (* x acc))))
The above procedure will be initially called with 1 as accumulator, like this:
(factorial 10 1)
=> 3628800
Notice that the accumulated value gets returned when the base case is reached, and that the acc parameter gets updated at each point in the recursive call. I had to add one extra parameter to the procedure, but this can be avoided by defining an inner procedure or a named let, for example:
(define (factorial x)
(let loop ((x x)
(acc 1))
(if (zero? x)
acc
(loop (sub1 x) (* x acc)))))
Answer from Óscar López on Stack Overflowrecursion - Tail recursive functions in Scheme - Stack Overflow
A function which checks if a function in tail-recursive?
recursion - scheme - Is function tail-recursive? - Stack Overflow
What is tail recursion?
In your first code block, f is not tail recursive. The recursive call to f has to happen, return its result, and then 1 or 7, depending on the branch, has to be added to the result. Since the call to recursive call to f has to return, and there are arbitrarily many recursive calls to f, this means that arbitrarily many stack frames must be allocated.
When you think of calling a function, it may help to imagine that you take a new piece of paper and write all the of the local variables and values on that piece of paper. When you're determining the result of the function, there may be recursive calls to the same function. If the call is a tail-call, then when you take a piece of paper for it, you can throw away the old sheet, since you don't need any of its values anymore.
For instance, consider two ways of computing the length of a list:
(define (length-1 list)
(if (null? list)
0
(+ 1
(length-1 (cdr list))))) ; recursive call, NOT a tail-call
In this implementation, you have to finish the recursive call to length-1, get its result, and add 1 to it. That means that you need somewhere to come back to after the recursive call. Now consider a tail-recursive version:
(define (length-2 list current-length)
(if (null? list)
current-length
(length-2 (cdr list) ; recursive call, IS a tail-call
(+ 1 current-length))))
In this version, once you start to make the recursive call to length-2, you don't need any context from the original call anymore. In fact, you could translate this to a loop by assigning (cdr list) to list, and assigning (+ 1 current-length) to current-length. You can reuse the same stack space. That's how tail call optimization (when the tail call is to the same function) is equivalent to a loop.
The tail recursive function will pass the accumulated result into each call, so that once the end condition is reached, the result can be immediately returned by that last call of the function.
A non-tail recursive function will require something be done to the result after it has been returned. So, each layer of recursion has to be remembered so that it can work its way back out to calculate the final result.
In your first example, you are adding to the result of the next call of f, so it is not tail recursive.
The second example is also adding to the next call of loop, so it is not tail recursive.
The third example, passes the final result in as an argument into the next function call, so it is tail recursive.