update from Atlas

This commit is contained in:
Luciano Ramalho
2015-04-09 00:51:55 -03:00
parent bdccc3269a
commit f4cdee2447
32 changed files with 270 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
===========================================
Tests for arithmetic progression generators
===========================================
Tests with built-in numeric types::
>>> ap = aritprog_gen(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = aritprog_gen(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
Tests with standard library numeric types::
>>> from fractions import Fraction
>>> ap = aritprog_gen(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = aritprog_gen(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0'), Decimal('0.1'), Decimal('0.2')]
Test producing an empty series::
>>> ap = aritprog_gen(0, 1, 0)
>>> list(ap)
[]

View File

@@ -0,0 +1,26 @@
"""
Demonstrate difference between Arithmetic Progression calculated
as a series of increments accumulating errors versus one addition
and one multiplication.
"""
from fractions import Fraction
from aritprog_v0 import ArithmeticProgression as APv0
from aritprog_v1 import ArithmeticProgression as APv1
if __name__ == '__main__':
ap0 = iter(APv0(1, .1))
ap1 = iter(APv1(1, .1))
ap_frac = iter(APv1(Fraction(1, 1), Fraction(1, 10)))
epsilon = 10**-10
iteration = 0
delta = next(ap0) - next(ap1)
frac = next(ap_frac)
while abs(delta) <= epsilon:
delta = next(ap0) - next(ap1)
frac = next(ap_frac)
iteration +=1
print('iteration: {}\tfraction: {}\tepsilon: {}\tdelta: {}'.
format(iteration, frac, epsilon, delta))

View File

@@ -0,0 +1,37 @@
import doctest
import importlib
import glob
TARGET_GLOB = 'aritprog*.py'
TEST_FILE = 'aritprog.rst'
TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
def main(argv):
verbose = '-v' in argv
for module_file_name in sorted(glob.glob(TARGET_GLOB)):
module_name = module_file_name.replace('.py', '')
module = importlib.import_module(module_name)
gen_factory = getattr(module, 'ArithmeticProgression', None)
if gen_factory is None:
gen_factory = getattr(module, 'aritprog_gen', None)
if gen_factory is None:
continue
test(gen_factory, verbose)
def test(gen_factory, verbose=False):
res = doctest.testfile(
TEST_FILE,
globs={'aritprog_gen': gen_factory},
verbose=verbose,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
tag = 'FAIL' if res.failed else 'OK'
print(TEST_MSG.format(gen_factory.__module__, res, tag))
if __name__ == '__main__':
import sys
main(sys.argv)

View File

@@ -0,0 +1,24 @@
"""
Arithmetic progression class
>>> ap = ArithmeticProgression(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
"""
class ArithmeticProgression:
def __init__(self, begin, step, end=None):
self.begin = begin
self.step = step
self.end = end # None -> "infinite" series
def __iter__(self):
result = type(self.begin + self.step)(self.begin)
forever = self.end is None
while forever or result < self.end:
yield result
result += self.step

View File

@@ -0,0 +1,44 @@
"""
Arithmetic progression class
# BEGIN ARITPROG_CLASS_DEMO
>>> ap = ArithmeticProgression(0, 1, 3)
>>> list(ap)
[0, 1, 2]
>>> ap = ArithmeticProgression(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = ArithmeticProgression(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
>>> from fractions import Fraction
>>> ap = ArithmeticProgression(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = ArithmeticProgression(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
# END ARITPROG_CLASS_DEMO
"""
# BEGIN ARITPROG_CLASS
class ArithmeticProgression:
def __init__(self, begin, step, end=None): # <1>
self.begin = begin
self.step = step
self.end = end # None -> "infinite" series
def __iter__(self):
result = type(self.begin + self.step)(self.begin) # <2>
forever = self.end is None # <3>
index = 0
while forever or result < self.end: # <4>
yield result # <5>
index += 1
result = self.begin + self.step * index # <6>
# END ARITPROG_CLASS

View File

@@ -0,0 +1,31 @@
"""
Arithmetic progression generator function::
>>> ap = aritprog_gen(1, .5, 3)
>>> list(ap)
[1.0, 1.5, 2.0, 2.5]
>>> ap = aritprog_gen(0, 1/3, 1)
>>> list(ap)
[0.0, 0.3333333333333333, 0.6666666666666666]
>>> from fractions import Fraction
>>> ap = aritprog_gen(0, Fraction(1, 3), 1)
>>> list(ap)
[Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
>>> from decimal import Decimal
>>> ap = aritprog_gen(0, Decimal('.1'), .3)
>>> list(ap)
[Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
"""
# BEGIN ARITPROG_GENFUNC
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
index = 0
while forever or result < end:
yield result
index += 1
result = begin + step * index
# END ARITPROG_GENFUNC

View File

@@ -0,0 +1,11 @@
# BEGIN ARITPROG_ITERTOOLS
import itertools
def aritprog_gen(begin, step, end=None):
first = type(begin + step)(begin)
ap_gen = itertools.count(first, step)
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen
# END ARITPROG_ITERTOOLS

View File

@@ -0,0 +1,14 @@
# BEGIN ARITPROG_ITERTOOLS
import itertools
def aritprog_gen(begin, step, end=None):
if end is not None and begin >= end:
return
yield type(begin + step)(begin)
tail_gen = itertools.count(begin+step, step)
if end is not None:
tail_gen = itertools.takewhile(lambda n: n < end, tail_gen)
for x in tail_gen:
yield x
# END ARITPROG_ITERTOOLS

View File

@@ -0,0 +1,13 @@
# BEGIN ARITPROG_ITERTOOLS
import itertools
def aritprog_gen(begin, step, end=None):
if end is not None and begin >= end:
return
yield type(begin + step)(begin)
tail_gen = itertools.count(begin+step, step)
if end is not None:
tail_gen = itertools.takewhile(lambda n: n < end, tail_gen)
yield from tail_gen
# END ARITPROG_ITERTOOLS

View File

@@ -0,0 +1,51 @@
"""
Fibonacci generator implemented "by hand" without generator objects
>>> from itertools import islice
>>> list(islice(Fibonacci(), 15))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
"""
# BEGIN FIBO_BY_HAND
class Fibonacci:
def __iter__(self):
return FibonacciGenerator()
class FibonacciGenerator:
def __init__(self):
self.a = 0
self.b = 1
def __next__(self):
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
def __iter__(self):
return self
# END FIBO_BY_HAND
# for comparison, this is the usual implementation of a Fibonacci
# generator in Python:
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
if __name__ == '__main__':
for x, y in zip(Fibonacci(), fibonacci()):
assert x == y, '%s != %s' % (x, y)
print(x)
if x > 10**10:
break
print('etc...')

View File

@@ -0,0 +1,24 @@
"""
Sentence: access words by index
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text) # <1>
def __getitem__(self, index):
return self.words[index] # <2>
def __len__(self, index): # <3>
return len(self.words)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text) # <4>

View File

@@ -0,0 +1,54 @@
==============================
Tests for a ``Sentence`` class
==============================
A ``Sentence`` is built from a ``str`` and allows iteration
word-by-word.
::
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
Any punctuation is skipped while iterating::
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
White space including line breaks are also ignored::
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
Accented Latin characters are also recognized as word characters::
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

View File

@@ -0,0 +1,25 @@
"""
Sentence: iterate over words using a generator function
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for word in self.words: # <1>
yield word # <2>
return # <3>
# done! <4>

View File

@@ -0,0 +1,21 @@
"""
Sentence: iterate over words using a generator function
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text # <1>
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for match in RE_WORD.finditer(self.text): # <2>
yield match.group() # <3>

View File

@@ -0,0 +1,44 @@
"""
Sentence: iterate over words using a generator expression
"""
# BEGIN SENTENCE_GENEXP
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
# END SENTENCE_GENEXP
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,65 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #1
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
# BEGIN SENTENCE_ITER
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self): # <1>
return SentenceIterator(self.words) # <2>
class SentenceIterator:
def __init__(self, words):
self.words = words # <3>
self.index = 0 # <4>
def __next__(self):
try:
word = self.words[self.index] # <5>
except IndexError:
raise StopIteration() # <6>
self.index += 1 # <7>
return word # <8>
def __iter__(self): # <9>
return self
# END SENTENCE_ITER
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,37 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #2
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
word_iter = RE_WORD.finditer(self.text) # <1>
return SentenceIter(word_iter) # <2>
class SentenceIter():
def __init__(self, word_iter):
self.word_iter = word_iter # <3>
def __next__(self):
match = next(self.word_iter) # <4>
return match.group() # <5>
def __iter__(self):
return self

View File

@@ -0,0 +1,36 @@
import doctest
import importlib
import glob
TARGET_GLOB = 'sentence*.py'
TEST_FILE = 'sentence.rst'
TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
def main(argv):
verbose = '-v' in argv
for module_file_name in sorted(glob.glob(TARGET_GLOB)):
module_name = module_file_name.replace('.py', '')
module = importlib.import_module(module_name)
try:
cls = getattr(module, 'Sentence')
except AttributeError:
continue
test(cls, verbose)
def test(cls, verbose=False):
res = doctest.testfile(
TEST_FILE,
globs={'Sentence': cls},
verbose=verbose,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
tag = 'FAIL' if res.failed else 'OK'
print(TEST_MSG.format(cls.__module__, res, tag))
if __name__ == '__main__':
import sys
main(sys.argv)

View File

@@ -0,0 +1,29 @@
""" Example from `Python: The Full Monty`__ -- A Tested Semantics for the
Python Programming Language
__ http://cs.brown.edu/~sk/Publications/Papers/Published/pmmwplck-python-full-monty/
"The following program, [...] seems to perform a simple abstraction over the
process of yielding:"
Citation:
Joe Gibbs Politz, Alejandro Martinez, Matthew Milano, Sumner Warren,
Daniel Patterson, Junsong Li, Anand Chitipothu, and Shriram Krishnamurthi.
2013. Python: the full monty. SIGPLAN Not. 48, 10 (October 2013), 217-232.
DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536
"""
# BEGIN YIELD_DELEGATE_FAIL
def f():
def do_yield(n):
yield n
x = 0
while True:
x += 1
do_yield(x)
# END YIELD_DELEGATE_FAIL
if __name__ == '__main__':
print('Invoking f() results in an infinite loop')
f()

View File

@@ -0,0 +1,24 @@
""" Example adapted from ``yield_delegate_fail.py``
The following program performs a simple abstraction over the process of
yielding.
"""
# BEGIN YIELD_DELEGATE_FIX
def f():
def do_yield(n):
yield n
x = 0
while True:
x += 1
yield from do_yield(x)
# END YIELD_DELEGATE_FIX
if __name__ == '__main__':
print('Invoking f() now produces a generator')
g = f()
print(next(g))
print(next(g))
print(next(g))