The classic programming puzzle, tackled a few different ways.
Test and lint:
pip install -r requirements.txt
black --check .
flake8 *.py
mypy *.py
pytestExample usage:
python3 -m naive 10
python3 -m strings 100
python3 -m cycles -h| import argparse | |
| from collections.abc import Generator | |
| from typing import Callable, List, Optional | |
| class Cli: | |
| """A uniform interface for all FizzBuzz methods. | |
| Examples | |
| -------- | |
| >>> def fizzbuzz(n: int) -> Generator[str, None, None]: | |
| ... yield from range(1, n + 1) | |
| >>> cli = Cli(func=fizzbuzz) | |
| >>> cli(["10"]) | |
| 1 2 3 4 5 6 7 8 9 10 | |
| """ | |
| def __init__(self, func: Callable[[int], Generator[str, None, None]]): | |
| """Create a new CLI instance wrapping a Fizzbuzz implementation. | |
| Parameters | |
| ---------- | |
| func | |
| A generator function implementing FizzBuzz. | |
| Attributes | |
| ---------- | |
| func: | |
| A generator function implementating FizzBuzz. | |
| parser: | |
| The prepared argparse instance. | |
| """ | |
| self.func = func | |
| self.parser = argparse.ArgumentParser( | |
| description="A FizzBuzz program." | |
| ) | |
| self.parser.add_argument( | |
| "n", type=int, help="Run FizzBuzz up to n (inclusive)." | |
| ) | |
| def __call__(self, args: Optional[List[str]] = None): | |
| """Call the CLI. | |
| Outputs each element in the FizzBuzz sequence on a newline. | |
| Parameters | |
| ---------- | |
| args | |
| Optional list of arguments to override ``sys.argv`` (for testing | |
| purposes, for instance). | |
| """ | |
| parsed = self.parser.parse_args(args=args) | |
| for i in self.func(parsed.n): | |
| print(i) |
| from collections.abc import Generator | |
| from itertools import cycle | |
| def fizzbuzz(n: int) -> Generator[str, None, None]: | |
| """ | |
| An FizzBuzz implementation which avoids division. | |
| Uses cycles to produce the output without having to check the modulus. | |
| Parameters | |
| ---------- | |
| n : int | |
| Run the FizzBuzz program between 1 and n (inclusive). | |
| Examples | |
| -------- | |
| >>> list(fizzbuzz(20)) | |
| ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', | |
| 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz'] | |
| """ | |
| fizzes = cycle(["", "", "Fizz"]) | |
| buzzes = cycle(["", "", "", "", "Buzz"]) | |
| for f, b, i in zip(fizzes, buzzes, range(1, n + 1)): | |
| yield f + b or str(i) | |
| if __name__ == "__main__": | |
| from cli import Cli | |
| cli = Cli(fizzbuzz) | |
| cli() |
| from collections.abc import Generator | |
| from itertools import cycle | |
| from typing import Tuple | |
| class FizzBuzz: | |
| """ | |
| A general purpose FizzBuzz generator. | |
| Examples | |
| -------- | |
| First, a simple a example where every fourth integer is replace with | |
| "Boar": | |
| >>> fizzbuzz = FizzBuzz(("Boar", 4)) | |
| >>> list(fizzbuzz(10)) | |
| ['1', '2', '3', 'Boar', '5', '6', '7', 'Boar', '9', '10'] | |
| Then the classic FizzBuzz: | |
| >>> fizzbuzz = FizzBuzz(("Fizz", 3), ("Buzz", 5)) | |
| >>> list(fizzbuzz(20)) | |
| ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', | |
| 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz'] | |
| """ | |
| def __init__(self, *args: Tuple[str, int]): | |
| """Create a new FizzBuzz generator. | |
| Parameters | |
| ---------- | |
| *args | |
| Pairs of word/integer to replace. See example. | |
| """ | |
| self.words = args | |
| def __call__(self, n: int) -> Generator[str, None, None]: | |
| """Run FizzBuzz. | |
| Parameters | |
| ---------- | |
| n : int | |
| Run the FizzBuzz program between 1 and n (inclusive). | |
| """ | |
| cycles = [ | |
| cycle([""] * (divisor - 1) + [word]) | |
| for word, divisor in self.words | |
| ] | |
| for *step, i in zip(*cycles, range(1, n + 1)): | |
| yield "".join(step) or str(i) | |
| fizzbuzz = FizzBuzz(("Fizz", 3), ("Buzz", 5)) | |
| if __name__ == "__main__": | |
| from cli import Cli | |
| cli = Cli(fizzbuzz) | |
| cli() |
| from collections.abc import Generator | |
| def fizzbuzz(n: int) -> Generator[str, None, None]: | |
| """ | |
| A naive FizzBuzz implementation. | |
| Uses if/else conditions to decide what to output at each stage. | |
| Parameters | |
| ---------- | |
| n : int | |
| Run the FizzBuzz program between 1 and n (inclusive). | |
| Examples | |
| -------- | |
| >>> list(fizzbuzz(20)) | |
| ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', | |
| 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz'] | |
| """ | |
| for i in range(1, n + 1): | |
| if i % 15 == 0: | |
| yield "FizzBuzz" | |
| elif i % 5 == 0: | |
| yield "Buzz" | |
| elif i % 3 == 0: | |
| yield "Fizz" | |
| else: | |
| yield str(i) | |
| if __name__ == "__main__": | |
| from cli import Cli | |
| cli = Cli(fizzbuzz) | |
| cli() |
| [tool.black] | |
| line-length = 79 | |
| target-version = ['py310'] | |
| [tool.pytest.ini_options] | |
| addopts = "--doctest-modules" | |
| doctest_optionflags = "NORMALIZE_WHITESPACE" |
| black | |
| flake8 | |
| mypy | |
| pytest |
| from collections.abc import Generator | |
| def fizzbuzz(n: int) -> Generator[str, None, None]: | |
| """ | |
| A one-liner FizzBuzz implementation. | |
| Builds an output string based on conditionals. | |
| Parameters | |
| ---------- | |
| n : int | |
| Run the FizzBuzz program between 1 and n (inclusive). | |
| Examples | |
| -------- | |
| >>> list(fizzbuzz(20)) | |
| ['1', '2', 'Fizz', '4', 'Buzz', 'Fizz', '7', '8', 'Fizz', 'Buzz', '11', | |
| 'Fizz', '13', '14', 'FizzBuzz', '16', '17', 'Fizz', '19', 'Buzz'] | |
| """ | |
| for i in range(1, n + 1): | |
| yield "Fizz" * (i % 3 == 0) + "Buzz" * (i % 5 == 0) or str(i) | |
| if __name__ == "__main__": | |
| from cli import Cli | |
| cli = Cli(fizzbuzz) | |
| cli() |