I am writing a prime generator, which is different from anyone in this link
generator in Python generating prime numbers
Here is my code
def sequence():
i = 1
while True:
i += 2
yield i
def prime_generator(n):
i = 2
it = sequence()
while i < n:
it= filter(lambda x: x % i, it)
i = next(it)
yield i
when i run something like
for i in prime_generator(50):
print(i)
It never dump 15, 33, sth like that for me. In a word, It gives me 2 and all odd numbers. what goes wrong here?
The problem is that i inside the lambda isn't "fixed"; when i changes in the outside scope, the previously created lambda functions all use the new value, and so they all do the same check: see if the current value from sequence() is divisible by the last found prime. Which they never are.
Wrapping it into another lambda and then calling it so that the value of i can be fixed works:
def prime_generator(n):
i = 2
it = sequence()
while i < n:
it = (lambda i: filter(lambda x: x % i, it))(i)
i = next(it)
yield i
Edit: also I don't believe your code (nor this) does yield 2, but that can be trivially fixed.
Related
I use PyCharm to realize a program which is aimed to generate primes. Code like this:
def _odd_iter():
n = 1
while True:
yield n
n = n + 2
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
it = _odd_iter()
yield 2
while True:
i = next(it)
yield i
it = filter(_not_divisible, it) # !!!!!!!!!!don't know how it works!!!!!!!!!!
for n in primes():
if n < 1000:
print(n)
else:
break
For me the annotated code is obscure, I dont know how it works and whether it is right, so I add a breakpoint on it and determine to debug. But it is a generator, I cannot see the detail numbers. What can I do?
filter(function or None, iterable) --> filter object
Return an iterator yielding those items of iterable for which function(item) is true. If function is None, return the items that are true.
You can't see the details because it returns an iterable object, do this to see what it returns:
it = list(filter(_not_divisible, it)) # or next(filter(...))
my code consists of me recreating the function 'filter()' and using it with a function to filter words longer than 5 characters. It worked with the actual function filter when I tried it btw...I'm using python 3+
def filter1(fn, a):
i = 0
while i != len(a):
u = i - 1
a[i] = fn(a[i], a[u])
i += 1
return a
def filter_long_words(l):
if len[l] > 5:
return [l]
listered = ['blue', 'hdfdhsf', 'dsfjbdsf', 'jole']
print(list(filter1(filter_long_words, listered)))
getting error
TypeError: filter_long_words() takes 1 positional argument but 2 were given
You are passing two parameters to fn (which refers to filter_long_words) here:
a[i] = fn(a[i], a[u])
But filter_long_words only accepts one parameter.
Notes:
You can loop through lists using for item in my_list, or if you want index as well for index, item in enumerate(my_list).
I think you might get an IndexError since u will be -1 in the first round of your loop.
The filter function can also be expressed as a list comprehension: (item for item in listered if filter_long_words(item))
My version of filter would look like this, if I have to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
for item in sequence:
if fn(item):
yield item
Since you have stated that you are using Python 3, this returns a generator instead of a list. If you want it to return a list:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
acc = []
for item in sequence:
if fn(item):
acc.append(item)
return acc
If you don't need to use a for loop:
def my_filter(fn, sequence):
if fn is None:
fn = lambda x: x
return (item for item in sequence if fn(item))
Your're calling fn with 2 parameters in filter1(fn, a), and since you've passed filter_long_words() to filter1 as fn, that triggers the error.
But there's more weird stuff:
I don't understand the magick of filter1 or what you were trying to
accomplish, but it seems to me that you don't have a clear idea what to do.
But if you want to mimic (somehow) how filter works, you have to return a
list which contains only items for which the fn function returns true. When
you know this, you can rewrite it - here are a few suggestions for rewrite
# explicit, inefficient and long, but straightforward version:
def filter1(fn, a):
new_list = []
for item in a:
if fn(item):
new_list.append(item):
return new_list
# shorter version using list comprehensions:
def filter1(fn, a):
return [item for item in a if fn(item)]
The filter_long_words function is wrong too - it should return True or
False. The only reason why it could work is because any non-empty list is
treated as True by python and default return value of a function is None,
which translates to False. But it's confusing and syntactically wrong to use
len[l] - the proper usage is len(l).
There are a few suggestions for rewrite, which all returns explicit boolean
values:
# unnecessary long, but self-explanatory:
def filter_long_words(l):
if len(l) > 5:
return True
else
return False
# short variant
def filter_long_words(l):
return len(l) > 5
You are calling "filter_long_words" with 2 parameter => fn(a[i], a[u]) also there is an error
def filter_long_words(l):
if **len[l]** > 5:
return [l]
len is builtin method it should be len(l)
I have a generator that will keep giving numbers that follow a specific formula. For sake of argument let's say this is the function:
# this is not the actual generator, just an example
def Generate():
i = 0
while 1:
yield i
i+=1
I then want to get a list of numbers from that generator that are below a certain threshold. I'm trying to figure out a pythonic way of doing this. I don't want to edit the function definition. I realize you could just use a while loop with your cutoff as the condition, but I'm wondering if there is a better way. I gave this a try, but soon realized why it wouldn't work.
l = [x for x in Generate() x<10000] # will go on infinitely
So is there a correct way of doing this.
Thanks
An itertools solution to create another iterator:
from itertools import takewhile
l = takewhile(lambda x: x < 10000, generate())
Wrap it in list() if you are sure you want a list:
l = list(takewhile(lambda x: x < 10000, generate()))
Or if you want a list and like inventing wheels:
l = []
for x in generate():
if x < 10000:
l.append(x)
else:
break
Wrap your generator within another generator:
def no_more_than(limit):
def limiter(gen):
for item in gen:
if item > limit:
break
yield item
return limiter
def fib():
a,b = 1,1
while 1:
yield a
a,b = b,a+b
cutoff_at_100 = no_more_than(100)
print list(cutoff_at_100(fib()))
Prints:
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
itertools.takewhile will only work until it comes across an item that does not fulfill the predicate. If you need to return all values from a possibly unordered iterable, I'd recommend using itertools.ifilter for Python 2.x as in
from itertools import ifilter
f = ifilter(lambda x: x < 400, gen())
f.next()
This filtered a generator yielding random integers between 0 and 400 as hoped.
FWIW itertools.ifilter was deprecated in Python 3.x in favour of the built-in filter() which has slightly different syntax for iterating
f = filter(lambda x: x < 400, gen())
next(f)
Wrap it on a zip generator of a range of the limit:
gen = range(100_000_000_000)
limit = 10
(z[1] for z in zip(range(limit), gen))
zip creates a tuple, that is the reason for z[1]
This may be used on for loops:
for g in (z[1] for z in zip(range(limit), gen)):
print(g)
Or you could use lambda:
wrap = lambda gen, limit: (z[1] for z in zip(range(limit), gen))
for g in wrap(gen, 10):
print(g)
Just use a counter for an infinite generator:
gen=Generate() # your generator function example
l=[gen.next() for i in range(100)]
But since it is a generator, use a generator expression:
seq=(gen.next() for i in xrange(100)) #need x in xrange in Python 2.x; 3.x use range
Edit
OK, then just use a controller:
def controler(gen,limit):
n=gen.next()
while n<limit:
yield n
n=gen.next()
seq=[i for i in controler(Generate(),100)]
replace Generate() with xrange(10000)
def all_gt(nums, n):
i = []
for c in nums:
if c > n:
i += c
return i
This is the code that i used and 'i' is supposed to return the value in nums larger than n.
But mine returns nothing inside the bracket. E.g.,
all_gt([1,2,3,4], 2) => [3,4]
Anyone knows how to fix?
Thanks
You declared i to be a list, so you need to append to it instead of adding.
def all_gt(nums, n):
i = []
for c in nums:
if c > n:
i.append(c) ## <----- note this
return i
Alternatively, you could have done this:
i += [c]
in place of the append.
Outdent your return statement so that it isn't executed as part of the loop.
I know there are easier ways to create a function which gives you the largest number in a list of numbers but I wanted to use recursion. When I call the function greatest, i get none. For example greatest([1,3,2]) gives me none. If there are only two elements in the list, I get the right answer so I know the problem must be with the function calling itself. Not sure why though.
def compare(a,b):
if a==b:
return a
if a > b:
return a
if a < b:
return b
def greatest(x):
if len(x)==0:
return 0
i=0
new_list=[]
while i< len(x):
if len(x)-i>1:
c=compare(x[i],x[i+1])
else:
c=x[i]
new_list.append(c)
i=i+2
if len(new_list)>1:
greatest(new_list)
else:
return new_list[0]
print greatest([1,3,2])
This line:
if len(new_list)>1:
greatest(new_list) # <- this one here
calls greatest but doesn't do anything with the value it returns. You want
return greatest(new_list)
After fixing that, your function seems to behave (although I didn't look too closely):
>>> import itertools
>>> for i in range(1, 6):
... print i, all(max(g) == greatest(g) for g in itertools.product(range(-5, 5), repeat=i))
...
1 True
2 True
3 True
4 True
5 True
A simple recursion can be like this :
from random import *
def greatest(x,maxx=float("-inf")):
if len(x)>0:
if x[0] > maxx:
maxx=x[0]
return greatest(x[1:],maxx)
else:
return maxx
lis=range(10,50)
shuffle(lis)
print greatest(lis) #prints 49