2

I'm trying to build a list of the first ten factorials

[1,1,2,6,24,120,720,5040,40320,362880]

using only list comprehension. Is that possible?

I don't know generators or lambda.

Here's my attempt, but I absolutely know why it's wrong:

lst = [1] + [i for i in range(1,10)]
lst[1:10] = [lst[i-1] * i for i in range(1,10)]
print(lst)
blz
  • 403
  • 6
  • 12
  • 1
    write a function that calculates the factorial (`def fact(n):...return res`) for a number `n` and then use a list comprehension like: `[fact(x) for x in range(10)]` – Ma0 Dec 09 '16 at 14:28
  • @Ev.Kounis Maybe the OP is asking us to write him a list comprehension that does not use *any* function ;) –  Dec 09 '16 at 14:30

4 Answers4

6

You can use math.factorial():

import math

[math.factorial(n) for n in range(10)]

Output:

>>> import math
>>> 
>>> [math.factorial(n) for n in range(10)]
[1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
ettanany
  • 19,038
  • 9
  • 47
  • 63
  • Yes, Python comes with batteries included :) –  Dec 09 '16 at 14:32
  • 2
    Although this is very concise, it's likely much, much slower for calculating large values of `n` since each call to `math.factorial(n)` **recomputes** all the intermediate values for 0! to (n-1)! every time it's called. – martineau Nov 03 '18 at 13:29
6

Just for fun:

One-liner mega-hack using list comprehension and an auxililary accumulator (the resulting list itself) to reuse previously computed value

s=[];  s=[s[-1] for x in range(1,10) if not s.append(x*s[-1] if s else 1)]

result:

[1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

note: The math.factorial answer is the way when elements are random. Here it's faster because we can reuse previous results.

There's also another drawback: the need to store all elements in a list because python does not allow if and assignment like C does. So we have to append to a list and negate the None it returns so if test is True

As I said: fun, but still a hack.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
2

Your attempt does not work because the list comprehension works element-wise, you cannot refer to lst[i-1] like that. There is a factorial function in math module, however, since you mentioned generators, you can use one like this

def mygenerator():
    total = 1
    current = 1
    while True:
        total *= current
        yield total
        current += 1

factorial = mygenerator()
output = [next(factorial) for i in range(10)]
blue_note
  • 27,712
  • 9
  • 72
  • 90
  • Is this time friendly? – Akinjide Dec 09 '16 at 14:37
  • 1
    Yes, in contrast to the recursive solution. It does not recompute values. It gives the factorials up to 500! in less than a second. With a recursive solution, you can't get to 20! – blue_note Dec 09 '16 at 14:39
  • 1
    @AkinjideBankole This solution computes `n!` by using `(n-1)!` which is already computed and does not do it all over again. So, yes. It should be even faster that the built-in `math.factorial()` for big n values – Ma0 Dec 09 '16 at 14:43
1

We could also use our own function but hey, the meaning of the comprehension lists is lost a bit but it is still functional

def factorialIte(n):
  factorial = 1
  for i in range(1,n+1):
    factorial = factorial*i
return factorial
# 10 is n_max
[factorialIte(n) for n in range(1,11)]
>>[1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
Cibas Mec
  • 41
  • 3