Last active
September 11, 2020 18:50
-
-
Save chrisma/02d713164ca0f6882f50 to your computer and use it in GitHub Desktop.
Fizzbuzz in too much detail
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /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