I have this segment in my Python course (which I wrote, see below) where I show prime number generator using an iterator written in two ways: as a generator function and as a class. Just for kicks, I cut and pasted the code to Codesters and discovered that, while it replicates Python3 syntax, it's not yet a complete implementation of the language. yield and next are not supported keyword and builtin respectively. No problem, I put in a workaround. http://mybizmo.blogspot.com/2017/02/prime-numbers.html (screen shots) https://www.codesters.com/preview/4183156df3fa49e1b12a4ab206d280af/ (runnable) I had my 5th-8th graders run the code having discovered the concept of "prime number" was already familiar. I threw out some numbers asking if they were prime. The 8th grade girl was not fooled by 51. I sketched "trial by division" very briefly on the whiteboard but did not do any careful reading of the Python code, as at this age it's enough to just eyeball the stuff and realize it cuts and pastes. The 2nd example below actually produces 3000 primes, not 30, despite the comment. :-D Thanks to Wes for links, still following 'em. Kirby PS: we also use MIT Scratch in this class. I'm still somewhat on a learning curve with that one. I got it to work for a Martian Math segment: http://controlroom.blogspot.com/2016/12/more-core.html What's Martian Math? http://wikieducator.org/Martian_Math http://www.4dsolutions.net/satacad/martianmath/toc.html # -*- coding: utf-8 -*- """ Created on Mon Mar 14 14:40:53 2016 @author: Kirby Urner Create an iterable that gives back successive prime numbers in two different ways: as a class and as a generator function. Trial by division involves accumulating all primes so far and admitting new candidates to the club only if no prime up to the sqrt of same, divides with no remainder. """ class Primes: def __init__(self): self.candidate = 1 self._primes_so_far = [2] # first prime, only even prime def __iter__(self): """I'm already an iterator so just return me as is""" return self def __next__(self): """proof I'm an iterator""" while True: self.candidate += 2 # check odds only from now on for prev in self._primes_so_far: if prev**2 > self.candidate: self._primes_so_far.append(self.candidate) return self._primes_so_far[-2] if not divmod(self.candidate, prev)[1]: # no remainder! break def primes(): """generate successive prime numbers (trial by division)""" candidate = 1 _primes_so_far = [2] # first prime, only even prime yield _primes_so_far[-1] while True: candidate += 2 # check odds only from now on for prev in _primes_so_far: if prev**2 > candidate: yield candidate # surrender control at this point! _primes_so_far.append(candidate) break if not divmod(candidate, prev)[1]: # no remainder! break # done looping #p = Primes() # class based iterator #print([next(p) for _ in range(30)]) # next 30 primes please! p = primes() # generator function based iterator print([next(p) for _ in range(3000)]) # next 30 primes please!
On Fri, Feb 17, 2017 at 1:53 PM, kirby urner <kirby.urner@gmail.com> wrote:
I have this segment in my Python course (which I wrote, see below) where I show prime number generator using an iterator written in two ways: as a generator function and as a class.
I have one other way of sharing the prime number trial by division thing: as coroutines. I owe David Beazley on this one. The coroutine decorator jumps us to the first yield so that we can send numbers in right away. primes weeds out composites, passing survivor primes to the printing coroutine (passed in as target). In my Tractor class, a simple object that plows a Field leaving a trail of Unicode characters (raster pattern), I used yield both to output and input (instances would gradually run low on fuel, but one could refill), but David advises against this pattern. http://mathforum.org/kb/message.jspa?messageID=9507410 (talks about Tractor Math) # -*- coding: utf-8 -*- """ Created on Thu Oct 13 13:48:52 2016 @author: Kirby Urner David Beazley: https://youtu.be/Z_OAlIhXziw?t=23m42s Trial by division, but this time the primes coroutine acts more as a filter, passing qualified candidates through to print_me, which writes to a file. """ def coroutine(func): """ Advances decorated generator function to the first yield """ def start(*args, **kwargs): cr = func(*args, **kwargs) cr.send(None) # or next(cr) or cr.__next__() return cr return start @coroutine def print_me(file_name): with open(file_name, 'w') as file_obj: while True: to_print = (yield) file_obj.write(str(to_print)+"\n") @coroutine def primes(target): _primes_so_far = [2] while True: candidate = (yield) for prev in _primes_so_far: if not divmod(candidate, prev)[1]: break if prev**2 > candidate: _primes_so_far.append(candidate) target.send(candidate) break output = print_me("primes.txt") p = primes(output) for x in range(3, 200, 2): # test odds 3-199 p.send(x) with open("primes.txt", 'r') as file_obj: print(file_obj.read())
Heres's a fix for the coroutine-based primes generator. It was trying to open a file that still wasn't closed, so would not print anything on first run (run it again, and the file from last time would print). I need to close that file at the end. The fix looks a bit inelegant, sending a -1 into the printer loop coroutine, triggering a StopIteration, which I catch. It should work now at least. Thanks to my buddy off list for testing it. # -*- coding: utf-8 -*- """ Created on Thu Oct 13 13:48:52 2016 @author: Kirby Urner David Beazley: https://youtu.be/Z_OAlIhXziw?t=23m42s Trial by division, but this time the primes coroutine acts more as a filter, passing qualified candidates through to print_me, which writes to a file. """ def coroutine(func): """ Advances decorated generator function to the first yield """ def start(*args, **kwargs): cr = func(*args, **kwargs) cr.send(None) # or next(cr) or cr.__next__() return cr return start @coroutine def print_me(file_name): with open(file_name, 'w') as file_obj: while True: to_print = (yield) if to_print == -1: break file_obj.write(str(to_print)+"\n") # print("File closed") @coroutine def primes(target): _primes_so_far = [2] while True: candidate = (yield) for prev in _primes_so_far: if not divmod(candidate, prev)[1]: break if prev**2 > candidate: _primes_so_far.append(candidate) target.send(candidate) break output = print_me("primes.txt") p = primes(output) for x in range(3, 200, 2): # test odds 3-199 p.send(x) try: output.send(-1) # tell print_me coroutine to quit, close file except: pass with open("primes.txt", 'r') as file_obj: print(file_obj.read())
participants (1)
-
kirby urner