Short answer: Tail recursion is desirable, but don't over-emphasize it.
Your original program is as tail recursive as you can get in Prolog. But there are more important issues: Correctness and termination.
In fact, many implementations are more than willing to sacrifice tail-recursiveness for other properties they consider more important. For example steadfastness.
But your attempted optimization has some point. At least from a historical perspective.
Back in the 1970s, the major AI language was LISP. And the corresponding definition would have been
(defun addone (xs)
(cond ((null xs) nil)
(t (cons (+ 1 (car xs))
(addone (cdr xs))))))
which is not directly tail-recursive: The reason is the cons
: In implementations of that time, its arguments were evaluated first, only then, the cons
could be executed. So rewriting this as you have indicated (and reversing the resulting list) was a possible optimization technique.
In Prolog, however, you can create the cons prior to knowing the actual values, thanks to logic variables. So many programs that were not tail-recursive in LISP, translated to tail-recursive programs in Prolog.
The repercussions of this can still be found in many Prolog textbooks.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…