Created
October 31, 2022 22:36
-
-
Save Per48edjes/a32f3c11ad8e4467fb47e6495c3c4025 to your computer and use it in GitHub Desktop.
Using generators, recursion, and decorators to return all even subsets of a sequence
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 | |
""" | |
Problem inspired by "Even Subsets" found here: | |
https://cs61a.org/assets/slides/28-Scheme_Lists_full.pdf | |
Playing around with recursion, generators, and decorators...of course, this | |
is completely for my own edification. I would not recommend writing code like | |
this in an applied setting... :) | |
""" | |
from typing import Iterable, Generator, List, Callable | |
def is_even(g: Generator[List, None, None]) -> Callable: | |
def wrapper(*args): | |
for subset in g(*args): | |
if sum(subset) % 2 == 0: | |
yield subset | |
return wrapper | |
@is_even | |
def subset_gen(s: Iterable[int]) -> Generator[List[int], None, None]: | |
""" | |
Generator that returns non-empty subsets of iterable `s` as lists, recursively | |
>>> t0 = all_subsets([]) | |
>>> next(t0) | |
Traceback (most recent call last): | |
StopIteration | |
>>> t1 = all_subsets((1,)) | |
>>> for e in t1: | |
... print(e) | |
[1] | |
>>> t2 = all_subsets((1, 2)) | |
>>> for e in t2: | |
... print(e) | |
[1] | |
[1, 2] | |
[2] | |
>>> t2 = all_subsets([k+1 for k in range(3)]) | |
>>> for e in t2: | |
... print(e) | |
[1] | |
[1, 2] | |
[2] | |
[1, 2, 3] | |
[2, 3] | |
[1, 3] | |
[3] | |
""" | |
# Encapsulate the recursion otherwise any decorator is applied to recursive | |
# calls, too, since the look up finds function name in global frame, wherein | |
# it's being decorated! | |
# | |
# See the following issue for more info: | |
# https://stackoverflow.com/questions/13915122/how-to-decorate-a-generator-in-python | |
def gen(s: Iterable[int]) -> Generator[List[int], None, None]: | |
if s: | |
first, rest = list(s[:1]), list(s[1:]) | |
yield first | |
for subset in gen(rest): | |
yield first + subset | |
yield subset | |
return gen(s) | |
def main(): | |
s = [3, 4, 5, 7] # Test case for decorated generator | |
for even_subset in subset_gen(s): | |
print(even_subset) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment