Skip to content

Instantly share code, notes, and snippets.

@chrisma
Last active September 11, 2020 18:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisma/02d713164ca0f6882f50 to your computer and use it in GitHub Desktop.
Save chrisma/02d713164ca0f6882f50 to your computer and use it in GitHub Desktop.
Fizzbuzz in too much detail
#! /usr/bin/env python3
# coding=utf-8
# Python version of
# http://www.tomdalling.com/blog/software-design/fizzbuzz-in-too-much-detail/
# Run it live at:
# https://www.pythonanywhere.com/gists/02d713164ca0f6882f50/fizzbuzz.py/ipython3/
### A Naïve Implementation
def fizzbuzz_naive():
for i in range(1,101):
# if i % 3 == 0 and i % 5 == 0:
if i % 15 == 0:
print('FizzBuzz')
elif i % 3 == 0:
print('Fizz')
elif i % 5 == 0:
print('Buzz')
else:
print(i)
### Don't Repeat Yourself (DRY) a
def fizzbuzz_dry():
for i in range(1,101):
fizz = (i % 3 == 0)
buzz = (i % 5 == 0)
print('FizzBuzz' if fizz and buzz else
'Fizz' if fizz else
'Buzz' if buzz
else i)
# More readable version:
# result = ''
# if fizz and buzz:
# result = 'FizzBuzz'
# elif fizz:
# result = 'Fizz'
# elif buzz:
# result = 'Buzz'
# else:
# result = i
# print(result)
### Don't Repeat Yourself (DRY) b
def fizzbuzz_dryer():
FIZZ = 'Fizz'
BUZZ = 'Buzz'
def divisible_by(numerator, denominator):
# return not numerator % denominator
return numerator % denominator == 0
for i in range(1,101):
fizz = divisible_by(i, 3)
buzz = divisible_by(i, 5)
print(FIZZ + BUZZ if fizz and buzz else
FIZZ if fizz else
BUZZ if buzz
else i)
### Parameterization a
def fizzbuzz_parameters(r=range(1,101), triggers=[('Fizz',3),('Buzz',5)]):
for i in r:
result = ''
for (text, divisor) in triggers:
# result += text if i % divisor == 0 else ''
if i % divisor == 0:
result += text
print(result or i)
### Parameterization b
def fizzbuzz_more_parameters(r=range(1,101),
triggers=[('Fizz', lambda i: i%3==0),
('Buzz', lambda i: i%5==0)]):
for i in r:
result = ''
for (text, predicate) in triggers:
if predicate(i):
result += text
print(result or i)
### Functional Programming (FP) a
def fizzbuzz_fp(r=range(1,101),
triggers=[('Fizz', lambda i: i%3==0),
('Buzz', lambda i: i%5==0)]):
def helper(i):
result = ''
for (text, predicate) in triggers:
if predicate(i):
result += text
return result or str(i)
# return [helper(i) for i in r]
return list(map(helper, r))
### Functional Programming (FP) b
def fizzbuzz_more_fp(r=range(1,101),
triggers=[('Fizz', lambda i: i%3==0),
('Buzz', lambda i: i%5==0)]):
# Alternative one-liner:
# return map(lambda i: ''.join([t for (t,p) in triggers if p(i)]) or str(i), range)
def helper(i):
parts = [t for (t,p) in triggers if p(i)]
return ''.join(parts) or str(i)
return list(map(helper, r))
### Lazy Generation
def fizzbuzz_generator(r=range(1,101),
triggers=[('Fizz', lambda i: i%3==0),
('Buzz', lambda i: i%5==0)]):
def helper(i):
parts = [t for (t,p) in triggers if p(i)]
return ''.join(parts) or str(i)
for elem in map(helper, r):
yield elem
if __name__ == "__main__":
import inspect, sys, timeit, unittest
from operator import itemgetter
class TestFizzBuzz(unittest.TestCase):
@property
def buffered_output(self):
if not hasattr(sys.stdout, "getvalue"):
self.fail("Need to run in buffered mode (buffer=True)")
return sys.stdout.getvalue().strip() # because stdout is an StringIO instance
def setUp(self):
self.desired_string = '1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\nBuzz'
self.desired_list = ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz', 'Fizz', '22', '23', 'Fizz', 'Buzz', '26', 'Fizz', '28', '29', 'FizzBuzz', '31', '32', 'Fizz', '34', 'Buzz', 'Fizz', '37', '38', 'Fizz', 'Buzz', '41', 'Fizz', '43', '44', 'FizzBuzz', '46', '47', 'Fizz', '49', 'Buzz', 'Fizz', '52', '53', 'Fizz', 'Buzz', '56', 'Fizz', '58', '59', 'FizzBuzz', '61', '62', 'Fizz', '64', 'Buzz', 'Fizz', '67', '68', 'Fizz', 'Buzz', '71', 'Fizz', '73', '74', 'FizzBuzz', '76', '77', 'Fizz', '79', 'Buzz', 'Fizz', '82', '83', 'Fizz', 'Buzz', '86', 'Fizz', '88', '89', 'FizzBuzz', '91', '92', 'Fizz', '94', 'Buzz', 'Fizz', '97', '98', 'Fizz', 'Buzz']
def test_naive(self):
fizzbuzz_naive()
self.assertEqual(self.buffered_output, self.desired_string)
def test_dry(self):
fizzbuzz_dry()
self.assertEqual(self.buffered_output, self.desired_string)
def test_dryer(self):
fizzbuzz_dryer()
self.assertEqual(self.buffered_output, self.desired_string)
def test_parameters(self):
fizzbuzz_parameters()
self.assertEqual(self.buffered_output, self.desired_string)
def test_more_parameters(self):
fizzbuzz_more_parameters()
self.assertEqual(self.buffered_output, self.desired_string)
def test_fizzbuzz_fp(self):
self.assertEqual(fizzbuzz_fp(), self.desired_list)
def test_fizzbuzz_more_fp(self):
self.assertEqual(fizzbuzz_more_fp(), self.desired_list)
def test_fizzbuzz_generator(self):
self.assertEqual(list(fizzbuzz_generator()), self.desired_list)
print('--- Performance Tests ---')
all_functions = inspect.getmembers(sys.modules[__name__],
inspect.isfunction)
results = []
for (name, func) in all_functions:
runs = timeit.repeat(name,
setup="from __main__ import " + name,
repeat=1000,
number=10000)
# lower bound for how fast your machine can run the given code snippet;
# higher values are typically not caused by variability in Python’s speed,
# but by other processes interfering with your timing accuracy.
# https://docs.python.org/3/library/timeit.html
results.append((name, min(runs)))
results = sorted(results, key=itemgetter(1))
max_len = max([len(name) for (name, f) in all_functions])
for (name, time) in results:
print(name.ljust(max_len), time)
print('\n--- Unit Tests ---')
unittest.main(buffer=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment