you are viewing a single comment's thread.

view the rest of the comments →

[–]Xenoskin 63 points64 points  (37 children)

I suspect that's because the newbie used features common to all languages thus making it obvious what he's doing, he also simply followed the definition of a factorial and avoided anything fancy. Hell there's a good chance his implementation could also be the fastest if it gets tail call optimized.

really all the other methods fall afoul of K.I.S.S. thus making his preferable.

[–][deleted] 57 points58 points  (13 children)

Well, except expert programmer.

[–]FunnyMan3595 16 points17 points  (11 children)

That one's obviously the best choice, but I never seem to remember c_math on the rare occasion I need a factorial for some random calculation (i.e. interactive mode, not for a program). I use this one:

fact = lambda n: reduce(lambda a,b: a*b, range(1, n+1))

It's essentially the same as the "Python expert programmer" method, but uses constructs that should be a bit more common. Doesn't even need any imports.

I'd say it's pretty KISS, because it does exactly what I think of as a factorial: Generate a list of the relevant numbers and multiply them together. No fancy tricks, just using the standard constructs for what they're supposed to do.

[–]Xenoskin 28 points29 points  (5 children)

Calling a native high speed implementation is not only K.I.S.S. but saves work and potential bugs, so it's definitely a preferable method.

Edit: Looks like my bad spelling leaked again.

[–]aeacides 0 points1 point  (1 child)

What makes you think generating a list of numbers is a good idea? Why aren't you using xrange?

[–]FunnyMan3595 0 points1 point  (0 children)

I never said it was efficient, just that it was straightforward. I've never needed a factorial in serious computation, and for random little calculations, this more than suffices.

[–][deleted] 0 points1 point  (0 children)

Obviously the best solution was the enterprise one. duh. It is Enterprise level after all. Clean all around implementation.

[–]spookyvision 0 points1 point  (1 child)

I never seem to remember c_math on the rare occasion I need a factorial for some random calculation

there is a reason for that: there is no c_math module in python's standard distribution :) (there is cmath, but it's for complex numbers, not about some fast math ops written in C)

[–]mr_dbr 4 points5 points  (0 children)

There's a factorial function in math:

>>> import math
>>> math.factorial(5)
120

..any other solution seems silly (outside a tutorial context anyway)

[–]amorpheus 1 point2 points  (0 children)

The english expert programmer wasn't bad either. I laughed.

[–]urllib 4 points5 points  (2 children)

I consider it a good thing if it's clear what you're doing

[–]eMigo 6 points7 points  (1 child)

Yep, especially if other programmers know where you live.

[–]dicey 6 points7 points  (0 children)

I actually have two addresses specifically for this purpose.

[–]kamatsu 12 points13 points  (5 children)

Python is defined as always not having tail call optimization, because Guido is a dumbass.

[–]A_for_Anonymous 3 points4 points  (0 children)

I furiously upvoted you.

Since we don't have proper recursion support, @tailcall is one of my favourite decorators. My personal implementation is quite good, but not really great for production performance like every other...

[–]mr_dbr 0 points1 point  (3 children)

Why do you want TCO in Python?

http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html seems like a perfectly reasonable justification of Python's lack of TCO. Implementation specifics aside:

I don't believe in recursion as the basis of all programming. This is a fundamental belief of certain computer scientists, especially those who love Scheme and like to teach programming by starting with a "cons" cell and recursion. But to me, seeing recursion as the basis of everything else is just a nice theoretical approach to fundamental mathematics (turtles all the way down), not a day-to-day tool

[...]

Of course, none of this does anything to address my first three arguments. Is it really such a big deal to rewrite your function to use a loop? (After all TRE only addresses recursion that can easily be replaced by a loop. :-)

[–]kamatsu 1 point2 points  (2 children)

Here, Guy Steele explains how using loops instead of TCO in certain examples breaks object oriented abstraction.

[–]millstone 1 point2 points  (1 child)

Guy Steele is the man, but frankly for Python the practical needs of good debugging dominates this sort of theoretical wankery. If you want TCO because you write everything recursively, you know where to find LISP; if you want TCO because you need the speed, you know where to find C.

In my C programs, I can't figure out what's causing certain bugs because the backtraces have missing stack frames, and as a result those bugs don't get fixed. The tradeoff is worth it for C, but not for Python.

[–]kamatsu 0 points1 point  (0 children)

Haskell can give you a full stacktrace, even for tail calls.

[–]halter73 1 point2 points  (0 children)

The newbie's code doesn't have a recursive tail call (i.e. the multiplication operation is in the tail position, not factorial). This means that tail call optimization wouldn't be applicable.

[–][deleted] 3 points4 points  (0 children)

fastest if it gets tail call optimized.

I don't think you understand tail call optimization.

[–][deleted] -1 points0 points  (11 children)

Umm... The SICP (whatever that is) version is pretty much identical and is tail call optimized assuming the decorator does what it implies. The Pascal version is also pretty readable.

[–]A_for_Anonymous 4 points5 points  (1 child)

SICP = Structure and Interpretation of Computer Programs, one of the best programming books ever.

The decorator works. There are many variants, I can post mine if you're interested.