|
| 1 | +from collections import deque, defaultdict, Counter |
| 2 | +import itertools |
| 3 | +import regex |
| 4 | +from typing import TypeVar, Generator, Iterable, Tuple, List |
| 5 | + |
| 6 | +_T = TypeVar("T") |
| 7 | + |
| 8 | +def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: |
| 9 | + elements_iter = iter(elements) |
| 10 | + last_element = next(elements_iter) |
| 11 | + for element in elements_iter: |
| 12 | + yield (last_element, element) |
| 13 | + last_element = element |
| 14 | + |
| 15 | +def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: |
| 16 | + elements_list = list(elements) |
| 17 | + for i in range(len(elements_list)): |
| 18 | + for j in range(i + 1, len(elements_list)): |
| 19 | + yield (elements_list[i], elements_list[j]) |
| 20 | + |
| 21 | +def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: |
| 22 | + elements_list = list(elements) |
| 23 | + for i in range(len(elements_list)): |
| 24 | + for j in range(len(elements_list)): |
| 25 | + if j == i: continue |
| 26 | + yield (elements_list[i], elements_list[j]) |
| 27 | + |
| 28 | + |
| 29 | +# yes, everyone else calls this "cumsum" but ohwell |
| 30 | +def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: |
| 31 | + rsum = [] |
| 32 | + elements_iter = iter(elements) |
| 33 | + |
| 34 | + if start is None: |
| 35 | + rsum.append(next(elements_iter)) |
| 36 | + else: |
| 37 | + rsum.append(start) |
| 38 | + |
| 39 | + for element in elements_iter: |
| 40 | + rsum.append(rsum[-1] + element) |
| 41 | + return rsum |
| 42 | + |
| 43 | + |
| 44 | +# I did some rough experiments with this version vs. a version that uses a deque, which |
| 45 | +# has efficient popleft, and it seems like this version actually wins because of how slow |
| 46 | +# iterating over a deque is (which you have to do if you want to use the results) |
| 47 | +def rolling_window( |
| 48 | + elements: Iterable[_T], |
| 49 | + window_size: int, |
| 50 | +) -> Generator[Tuple[_T, ...], None, None]: |
| 51 | + current_window = [] |
| 52 | + for element in elements: |
| 53 | + current_window.append(element) |
| 54 | + if len(current_window) > window_size: |
| 55 | + del current_window[0] |
| 56 | + if len(current_window) == window_size: |
| 57 | + yield current_window |
| 58 | + |
| 59 | + |
| 60 | + |
| 61 | +# this is like slightly borked because it doesn't get negative numbers |
| 62 | +# oh well i guess |
| 63 | +nums_regex = regex.compile("([^\\d]*)((?P<nums>\\d+)([^\\d]*))*") |
| 64 | + |
| 65 | +def nums(s): |
| 66 | + m = nums_regex.match(s) |
| 67 | + vals = m.capturesdict()["nums"] |
| 68 | + return [int(x) for x in vals] |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +# underscored names are in case functions get shadowed by accident |
| 73 | +adjp = _adjp = adjacent_pairs |
| 74 | +ap = _ap = all_pairs |
| 75 | +at = _at = all_tuples |
| 76 | +rw = _rw = rolling_window |
| 77 | +rsum = _rsum = rolling_sum |
| 78 | + |
| 79 | +dd = _dd = defaultdict |
| 80 | +ctr = _ctr = Counter |
0 commit comments