Throw the cat among the pigeons
Cecil Westerhof
Cecil at decebal.nl
Sat May 2 10:20:29 EDT 2015
I am throwing the cat among the pigeons. ;-)
In another thread I mentioned that I liked to have tail recursion in
Python. To be clear not automatic, but asked for.
Looking at the replies I did hit a nerve. But I still want to
continue.
Some things are better expressed recursively for the people reading
the code. But there are two problems with that:
- You can get out of stack space
- It is less efficient
Most of the time the first problem is the most important.
When I write factorial (I know it is already written, but I use it as
an example to show a point), the recursive variant can not be called
with 1.000 without tail recursion. So for functions that could go very
deep, tail recursion would be a blessing.
By the way: I think that even if the recursion does not go further as
500, it is still a good idea to use tail recursion. Why use stack
space when it is not necessary?
But to my surprise tail recursion could even be more efficient. I
wrote two different versions of factorial with self implemented tail
recursion. For bigger values both are more efficient. And I expect
that if the tail recursion is done by the compiler instead of by hand,
it will be a little faster still.
This is the output a run of my code:
15:05:50: Start with the time needed to calculate 100000 times
15:05:50: Timing factorial_iterative (985): 32.265420768002514
15:06:22: Timing factorial_recursive (985): 58.381072121992474
15:07:21: Timing factorial_recursive_old (985): 64.46238571999129
15:08:25: Timing factorial_tail_recursion (985): 40.43312480399618
15:09:06: Timing factorial_tail_recursion_old(985): 39.70765891499468
15:09:45: Start with the time needed to calculate 1 times
No recursive, because without tail recursion you would run out of stack space
15:09:45: Timing factorial_iterative (100000): 3.9112528519763146
15:09:49: Timing factorial_tail_recursion (100000): 3.928693111985922
15:09:53: Timing factorial_tail_recursion_old(100000): 4.305187558988109
15:09:58: Timing factorial_iterative (200000): 18.081113666004967
15:10:16: Timing factorial_tail_recursion (200000): 16.660855480993632
15:10:32: Timing factorial_tail_recursion_old(200000): 18.169589380006073
15:10:51: Timing factorial_iterative (300000): 41.79109025900834
15:11:32: Timing factorial_tail_recursion (300000): 38.368264676013496
15:12:11: Timing factorial_tail_recursion_old(300000): 41.646923307009274
15:12:52: Timing factorial_iterative (400000): 78.35287749301642
15:14:11: Timing factorial_tail_recursion (400000): 73.17889478098368
15:15:24: Timing factorial_tail_recursion_old(400000): 89.64840986899799
15:16:53: Timing factorial_iterative (500000): 154.76221033901675
15:19:28: Timing factorial_tail_recursion (500000): 130.3837693700043
15:21:39: Timing factorial_tail_recursion_old(500000): 131.41286378499353
15:23:50: These result show that tail recursion can be interesting
They show also that the way you use tail recursion is important
As said the most important reason is that code is often more elegant
when written recursively, but you cannot do that if it is possible
that the recursion can go very deep. But if recursively code would be
more elegant and faster, then it would really be interesting to have.
I would not opt for automatically using tail recursion, but only when
the programmer says so. And if it is not possible, that should be an
error.
To make sure it was not a fluke, I ran it again:
16:01:30: Start with the time needed to calculate 100000 times
16:01:30: Timing factorial_iterative (985): 31.465190444985637
16:02:01: Timing factorial_recursive (985): 54.562154764978914
16:02:56: Timing factorial_recursive_old (985): 55.56128695001826
16:03:52: Timing factorial_tail_recursion (985): 36.27355203201296
16:04:28: Timing factorial_tail_recursion_old(985): 40.36879472099827
16:05:08: Start with the time needed to calculate 1 times
No recursive, because without tail recursion you would run out of stack space
16:05:08: Timing factorial_iterative (100000): 3.764512833993649
16:05:12: Timing factorial_tail_recursion (100000): 3.8083034529990982
16:05:16: Timing factorial_tail_recursion_old(100000): 4.107901128008962
16:05:20: Timing factorial_iterative (200000): 16.076719653996406
16:05:36: Timing factorial_tail_recursion (200000): 16.108007609989727
16:05:52: Timing factorial_tail_recursion_old(200000): 17.71343147099833
16:06:10: Timing factorial_iterative (300000): 37.82596729800571
16:06:48: Timing factorial_tail_recursion (300000): 40.308226338995155
16:07:28: Timing factorial_tail_recursion_old(300000): 41.254319412022596
16:08:09: Timing factorial_iterative (400000): 77.01277641401975
16:09:26: Timing factorial_tail_recursion (400000): 73.4060631209868
16:10:40: Timing factorial_tail_recursion_old(400000): 80.26402168802451
16:12:00: Timing factorial_iterative (500000): 131.84731978402124
16:14:12: Timing factorial_tail_recursion (500000): 125.31950747498195
16:16:17: Timing factorial_tail_recursion_old(500000): 133.39186109701404
16:18:30: These result show that tail recursion can be interesting
They show also that the way you use tail recursion is important
--
Cecil Westerhof
Senior Software Engineer
LinkedIn: http://www.linkedin.com/in/cecilwesterhof
More information about the Python-list
mailing list