2

While writing a python program for calculating the sum of primes between 2 to n (both inclusive), I am getting the error "The generator is not callable"

Can anyone help with the error or suggest what am I missing here?

n = int(input())

sum = 0
sum = sum(list(filter((lambda n: n%y != 0 for y in range(2, n)), range(2, n+1))))

print(sum)
kederrac
  • 15,339
  • 4
  • 25
  • 45
Vandit
  • 99
  • 6
  • 1
    The first argument that is passed to `filter` is the generator `(lambda n: n%y != 0 for y in range(2,n))` but should be a function taking one argument returning True or False. – nicoring Jan 09 '20 at 10:06
  • 3
    bad naming, `sum` is a *built-in name* – scleronomic Jan 09 '20 at 10:09

6 Answers6

3

1. first error: "TypeError: 'generator' object is not callable"

this error raise because you pass to filter build-in method a generator, you "cover" the lambda method with parentheses:

filter((lambda n: n%y != 0 for y in range(2,n)), range(2,n+1))

Solution: "uncover" the parentheses

filter(lambda n: n%y != 0 for y in range(2,n), range(2,n+1))

2. second error: "SyntaxError: Generator expression must to be parenthesized":

lambda n: n%y != 0 for y in range(2,n)

the error is clear: n%y != 0 for y in range(2,n) is a generator and needs to be be parenthesized, here you want to test if the number is a prime so you want to check if all the values from your generator are True

Solution: use the build-in method all

lambda n: all(n%y != 0 for y in range(2,n))

3. last error: "TypeError: 'int' object is not callable"

sum=0

this is happening because you are using sum build-in method name for your variable sum, so the sum is not anymore the built-in method but an integer

Solution: another name for your variable:

n_prime_sum = 0

here is your code with the fixes:

n = int(input())

n_prime_sum = 0
n_prime_sum = sum(list(filter(lambda n: all(n%y != 0 for y in range(2,n)), range(2,n+1))))
n_prime_sum
# n = 10

output:

17

also, you can define a function that generates all the prime btw 2 and n (both inclusive) then apply the sum build-in method on this function:

# original code by David Eppstein, UC Irvine, 28 Feb 2002
# with comments by Eli Bendersky, https://stackoverflow.com/a/568618

def gen_primes(n):
    """Much more efficient prime generation, the Sieve of Eratosthenes"""

    D = {}

    q = 2   # The running integer that's checked for primeness

    while q <= n:
        if q not in D:
            # q is a new prime.
            # Yield it and mark its first multiple that isn't
            # already marked in previous iterations
            # 
            yield q
            D[q * q] = [q]
        else:
            # q is composite. D[q] is the list of primes that
            # divide it. Since we've reached q, we no longer
            # need it in the map, but we'll mark the next 
            # multiples of its witnesses to prepare for larger
            # numbers
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]

        q += 1

n = int(input())
n_prime_sum = sum(gen_primes(n))
print(n_prime_sum)
# n = 10

output:

17

most of the code for the function gen_prime comes from here

Will Ness
  • 62,652
  • 8
  • 86
  • 167
kederrac
  • 15,339
  • 4
  • 25
  • 45
2

Well, in my imagination this approach being optimal would

  • only divide with (already) known primes (and not every number)
  • up to the square root of the given number

import math

primes=[2]

def isprime(n):         # NB! this is fragile,
  s=int(math.sqrt(n))   #     relies on `primes` being
  for p in primes:      #     already fully grown
    if n%p==0:          #     up to the sqrt(n)
      return False
    if p>s:
      return True

maxn=19
for i in range(3,maxn+1):
  if isprime(i):
    primes.append(i)

print(primes)
print(sum(primes))

Then comes the uglification make it lambda part

isprime=lambda n:all(n%p!=0 for p in primes)

works, but it misses the sqrt magic, then

isprime=lambda n:all(n%p!=0 for p in (p for p in primes if p<=int(math.sqrt(n))))

which calculates int(math.sqrt(n)) for all known primes, but that could be a one-time parameter instead, either for a filter() or for an innner lambda (this latter one is shown here):

isprime=lambda n:all(n%p!=0 for p in (lambda s:(p for p in primes if p<=s))(int(math.sqrt(n))))

I still do not like the fact that it compares all primes to s, stopping in the middle may be more efficient, getting a slice with index(next(...)) can do that:

isprime=lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))])

just it remains ugly that next() and index() traverse a part of the list twice.

Then comes the outer loop, I would use reduce() for that from functools, as the accumulator of the reduction could be the actual list of primes, and then it would not be a separate variable.

Step 0 would be making it in a minimalist way, still with the variable primes lying around, but using the terrible trick with tuples, (do,some,things,result)[3] will do the things, and evaluate to the result:

primes=[2]
isprime=lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))])
maxn=19
ps=functools.reduce(lambda primes,n:(primes.append(n) if isprime(n) else None,primes)[1],range(3,maxn+1),primes)
print(ps)
print(primes)
print(sum(primes)) # ps and primes are the same list actually

and then finalize the monster:

import math
import functools

plist=lambda maxn:functools.reduce(lambda primes,n:(primes.append(n) if (lambda n:all(n%p!=0 for p in primes[:(lambda s:primes.index(next(p for p in primes if p>s)))(int(math.sqrt(n)))]))(n) else None,primes)[1],range(3,maxn+1),[2])

primes=plist(19)
print(primes)
print(sum(primes))

Test: https://ideone.com/0y7dN9
Output:

[2, 3, 5, 7, 11, 13, 17, 19]
77

The takeaway message would be: lambdas can be useful, but actually you do not always have to use them.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
tevemadar
  • 9,697
  • 2
  • 15
  • 36
  • you should probably comment on the fragility of `isprime()` in your opening code somehow, i.e. if we'd insert the call `isprime(19)` right after `maxn=19`, it'd return false (or some other error, like unspecified return value?). – Will Ness Jan 09 '20 at 14:42
  • @WillNess yes, thanks, you are right. Originally I had that in mind, but completely forgot afterwards, I was focusing on getting to the one-liner (that one is "safe" as everything happens "inside"). Also, the thing works for `n>2` only. – tevemadar Jan 09 '20 at 16:35
1

Your solution is almost correct. But you need to use all inside the lambda function, because you want all the n%y != 0 statements to be true:

sum(list(filter((lambda n: all(n%y != 0 for y in range(2,n))), range(2,n+1))))

As pointed out in the comments, you could improve the efficiency of your code by checking only the divisors which are not greater than the square root of the number:

import math
sum(list(filter((lambda n: all(n%y != 0 for y in range(2,int(math.sqrt(n))+1))), range(2,n+1))))

Explanation

  • n%y != 0 for y in range(2,n) is a generator, not a boolean value!
  • all(n%y != 0 for y in range(2,n)) is a boolean value corresponding to n%2 != 0 and n%3 != 0 and ... and n%(n-1) != 0
Will Ness
  • 62,652
  • 8
  • 86
  • 167
Riccardo Bucco
  • 6,551
  • 3
  • 13
  • 32
  • 1
    You could apply the common "trick" of running the inner loop to the square root only, something like `for y in range(2,int(math.sqrt(n))+1)` – tevemadar Jan 09 '20 at 10:24
  • 1
    @tevemadar Of course, I simply respected the solution of the user and fixed it :) – Riccardo Bucco Jan 09 '20 at 10:25
  • you can *add* the improved one. – Will Ness Jan 09 '20 at 10:26
  • @WillNess Sure. Which one should I add? There are many algorithms, not only those using brute force... – Riccardo Bucco Jan 09 '20 at 10:28
  • with the sqrt, no? that's a start. – Will Ness Jan 09 '20 at 10:29
  • @WillNess My point is that I was simply fixing the code in the question. My intention was not to give the most efficient algorithm, because the user did not ask for it. There are many algorithms to check if a number is prime, and the one with the sqrt trick is not the fastest one. So why should I prefer that one over another one? Anyway I added it, as you suggested. – Riccardo Bucco Jan 09 '20 at 10:34
  • yes, you've explained it before, that's why I suggested *adding* it, as opposed to replacing. and it's the simplest one while still being very significant improvement - the greatest bang for the fewest bucks so to speak. :) – Will Ness Jan 09 '20 at 14:12
1

A slightly cleaner version:

n = int(input())
nums = range(2, n + 1)
for i in range(2, n + 1):
    nums = list(filter(lambda x: x == i or x % i, nums))

print(sum(nums))
Harshal Parekh
  • 4,830
  • 4
  • 13
  • 34
1

You can also write it like this with list comprehension:

from math import sqrt, floor

sum(( p 
      for p in range(2, n+1) 
      if all( p%q != 0 for q in range(2, floor(sqrt(p))+1) ) ))

You don't need to check all the numbers, just the ones until the square root of the own number.

Example: You can find out that 10 = 2 * 5 is non a prime by checking 10 % 2 == 0 or 10 % 5 == 0. With the square root limit, you'd only check until 3, but that's fine since you'd already have seen the 2, and defined 10 as not prime.

If this application is for you to explore, you might want to check the sieve of Eratosthenes algorithm to find the prime numbers, which is considered one of the most efficient ways to find small primes.

However, if you just need to find the primes and sum them, you can always use a library like gmpy2. Check this answer to know more.

MkWTF
  • 1,272
  • 7
  • 10
  • 1
    sieve of Eratosthenes is the most efficient one for the "small" (under 12 digits, say) numbers. sieve of trial division -- not so much. – Will Ness Jan 09 '20 at 10:27
0

After reading all the answers and trying/testing myself, I have redesigned the answer, which you can modify as per your need:

# get the user input
n = int(input())

from functools import reduce

# create a list with all the primes within the input number range
n_prime = range(2, n + 1)
for i in range(2, n + 1):
    n_prime = list(filter(lambda x: x == i or x % i, n_prime))

# print the list of primes obtained from above
print(n_prime)

# let's add all these numbers in the above list and print the summed value
sum_n_prime = reduce(lambda x, y : x + y, n_prime)
print(sum_n_prime)
Manoj Kumar
  • 3,773
  • 1
  • 19
  • 27