3 ms·
> Try writing something basic as fib as a recursive function and then in a loop. One is elegant and the other is kludgy. At the same time, one is slow as hell,
by dozzie 9y ago
> Try writing something basic as fib as a recursive function and then in a loop. One is elegant and the other is kludgy.
At the same time, one is slow as hell, because recalculates the very same
value multiple times (unless you return a tuple, in which case it loses its
elegance). You've chosen a bad example.
Also, remember that the opinion comes from a guy who mistakes tuples for
lists with some unrelated property (being immutable), even though he had
a few opportunities to fix his initial misunderstanding. Anything coming from
van Rossum about functional programming should be taken with a grain of salt.
- heavenlyblue 9y agoOne simple reason to not have tail-call elimination is the "nice stack traces" that are so useful in Python.
- dozzie 9y agoThis one reason is somewhat OK, but note that these "nice stack traces" are not really that useful in functional languages (Erlang being a prominent example). It's a self-inflicted thing that full stack traces are that useful in Python.
- catnaroek 9y agoStack traces are not a very good argument. As a general rule, you should write your programs to be flawless. Only if you are unsure about a particular piece of code, then can you manually add debugging information, at your own expense.
- nybble41 9y ago> At the same time, [recursive fib] is slow as hell, because recalculates the very same value multiple times... That depends on the language and choice of evaluation strategy. For example, in Haskell this recursive form is both elegant and fast: fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
- dozzie 9y agoThere is this curious divergence between what people writing in Haskell consider elegant and what virtually everybody else considers elegant. I can't make heads or tails of your example, even though I'm versed enough in functional and imperative programming and I know what the code should do. But yes, in languages that cache the call results by default the original formula will be fast.
- nybble41 9y agoApart from non-strict evaluation, the example doesn't use any primitives which are not extremely common in any functional programming language: -- list constructor 1 : 2 : 3 : [] == [1,2,3] -- remove first item from a list tail [1,2,3] == [2,3] -- combine two lists "pointwise" zipWith (+) [1,2] [3,4] == [1+3,2+4] If you line up the Fibonacci numbers starting from the first element and the Fibonacci numbers starting from the second element, and add the corresponding elements, you get the remaining elements of the Fibonacci sequence. Visually: 1, 1, (2, 3, 5, 8, 13, ...) -- fibs 1, (2, 3, 5, 8, 13, ...) -- tail fibs + ========================== (2, 3, 5, 8, 13, ...) -- zipWith (+) This declaration: fibs = 1 : 1 : zipWith (+) fibs (tail fibs) is merely the previous recurrence diagram translated into code. That is why I consider it an elegant definition.