Skip to content
Snippets Groups Projects
Commit fcb7dfa4 authored by Piotr Maślanka's avatar Piotr Maślanka
Browse files

2.7.13 - header of half_cartesian changed

parent 5d3cb759
No related branches found
No related tags found
No related merge requests found
# v2.7.13 # v2.7.13
* _TBA_ * bugfix release: header of `half_cartesian` changed
# v2.7.12 # v2.7.12
......
...@@ -59,6 +59,9 @@ if object A collides with B then B collides with A). ...@@ -59,6 +59,9 @@ if object A collides with B then B collides with A).
It helps you save time during computationally intensive operations. It helps you save time during computationally intensive operations.
This routine will return a iterator of tuple containing two elements
from the same set (ie. it will do something like a cartesian power of two).
.. autofuction:: satella.coding.sequences.half_cartesian .. autofuction:: satella.coding.sequences.half_cartesian
skip_first skip_first
......
__version__ = '2.7.13_a1' __version__ = '2.7.13'
from .choose import choose from .choose import choose
from .iterators import infinite_counter, take_n, is_instance, skip_first, zip_shifted, stop_after, \ from .iterators import infinite_counter, take_n, is_instance, skip_first, zip_shifted, stop_after, \
iter_dict_of_list, shift, other_sequence_no_longer_than, count, even, odd iter_dict_of_list, shift, other_sequence_no_longer_than, count, even, odd
from .sequences import is_last, add_next, half_product, group_quantity from .sequences import is_last, add_next, half_cartesian, group_quantity
__all__ = ['choose', 'infinite_counter', 'take_n', 'is_instance', 'is_last', 'add_next', __all__ = ['choose', 'infinite_counter', 'take_n', 'is_instance', 'is_last', 'add_next',
'half_product', 'skip_first', 'zip_shifted', 'stop_after', 'group_quantity', 'half_cartesian', 'skip_first', 'zip_shifted', 'stop_after', 'group_quantity',
'iter_dict_of_list', 'shift', 'other_sequence_no_longer_than', 'count', 'iter_dict_of_list', 'shift', 'other_sequence_no_longer_than', 'count',
'even', 'odd'] 'even', 'odd']
import typing as tp __all__ = ['is_last', 'add_next', 'half_product', 'group_quantity']T = tp.TypeVar('T')U = tp.TypeVar('U') # shamelessly copied from https://medium.com/better-programming/is-this-the-last-element-of-my-python-for-loop-784f5ff90bb5 def is_last(lst: tp.Iterable[T]) -> tp.Generator[tp.Tuple[bool, T], None, None]: """ Return every element of the list, alongside a flag telling is this the last element. Use like: >>> for is_last, element in is_last(my_list): >>> if is_last: >>> ... :param lst: list to iterate thru :return: a generator returning (bool, T) """ iterable = iter(lst) ret_var = next(iterable) for val in iterable: yield False, ret_var ret_var = val yield True, ret_var def add_next(lst: tp.Iterable[T], wrap_over: bool = False, skip_last: bool = False) -> tp.Iterator[ tp.Tuple[T, tp.Optional[T]]]: """ Yields a 2-tuple of given iterable, presenting the next element as second element of the tuple. The last element will be the last element alongside with a None, if wrap_over is False, or the first element if wrap_over was True Example: >>> list(add_next([1, 2, 3, 4, 5])) == [(1, 2), (2, 3), (3, 4), (4, 5), (5, None)] >>> list(add_next([1, 2, 3, 4, 5], True)) == [(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] :param lst: iterable to iterate over :param wrap_over: whether to attach the first element to the pair of the last element instead of None :param skip_last: if this is True, then last element, alongside with a None, won't be output """ iterator = iter(lst) try: first_val = prev_val = next(iterator) except StopIteration: return for val in iterator: yield prev_val, val prev_val = val if wrap_over: yield prev_val, first_val else: if not skip_last: yield prev_val, None def half_product(seq1: tp.Iterable[T], seq2: tp.Iterable[U]) -> tp.Generator[ tp.Tuple[T, U], None, None]: """ Generate half of the Cartesian product of both sequences. Useful when you have a commutative operation that you'd like to execute on both elements (eg. checking for collisions). Example: >>> list(half_cartesian([1, 2, 3], [1, 2, 3])) == \ >>> [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] :param seq1: First sequence :param seq2: Second sequence """ for i, elem1 in enumerate(seq1): for j, elem2 in enumerate(seq2): if j >= i: yield elem1, elem2 def group_quantity(length: int, seq: tp.Iterable[T]) -> tp.Generator[tp.List[T], None, None]: """ Slice an iterable into lists containing at most len entries. Eg. >>> assert list(group_quantity(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) == [[1, 2, 3], [4, 5, 6], >>> [7, 8, 9], [10]] :param length: length for the returning sequences :param seq: sequence to split """ entries = [] for elem in seq: if len(entries) < length: entries.append(elem) else: yield entries entries = [elem] if entries: yield entries import typing as tp __all__ = ['is_last', 'add_next', 'half_cartesian', 'group_quantity']T = tp.TypeVar('T')U = tp.TypeVar('U') # shamelessly copied from https://medium.com/better-programming/is-this-the-last-element-of-my-python-for-loop-784f5ff90bb5 def is_last(lst: tp.Iterable[T]) -> tp.Generator[tp.Tuple[bool, T], None, None]: """ Return every element of the list, alongside a flag telling is this the last element. Use like: >>> for is_last, element in is_last(my_list): >>> if is_last: >>> ... :param lst: list to iterate thru :return: a generator returning (bool, T) """ iterable = iter(lst) ret_var = next(iterable) for val in iterable: yield False, ret_var ret_var = val yield True, ret_var def add_next(lst: tp.Iterable[T], wrap_over: bool = False, skip_last: bool = False) -> tp.Iterator[ tp.Tuple[T, tp.Optional[T]]]: """ Yields a 2-tuple of given iterable, presenting the next element as second element of the tuple. The last element will be the last element alongside with a None, if wrap_over is False, or the first element if wrap_over was True Example: >>> list(add_next([1, 2, 3, 4, 5])) == [(1, 2), (2, 3), (3, 4), (4, 5), (5, None)] >>> list(add_next([1, 2, 3, 4, 5], True)) == [(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] :param lst: iterable to iterate over :param wrap_over: whether to attach the first element to the pair of the last element instead of None :param skip_last: if this is True, then last element, alongside with a None, won't be output """ iterator = iter(lst) try: first_val = prev_val = next(iterator) except StopIteration: return for val in iterator: yield prev_val, val prev_val = val if wrap_over: yield prev_val, first_val else: if not skip_last: yield prev_val, None def half_cartesian(seq1: tp.Iterable[T]) -> tp.Iterator[tp.Tuple[T, T]]: """ Generate half of the Cartesian product of both sequences. Useful when you have a commutative operation that you'd like to execute on both elements (eg. checking for collisions). Example: >>> list(half_cartesian([1, 2, 3], [1, 2, 3])) == \ >>> [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)] :param seq1: First sequence :param seq2: Second sequence """ for i, elem1 in enumerate(seq1): for j, elem2 in enumerate(seq1): if j >= i: yield elem1, elem2 def group_quantity(length: int, seq: tp.Iterable[T]) -> tp.Generator[tp.List[T], None, None]: """ Slice an iterable into lists containing at most len entries. Eg. >>> assert list(group_quantity(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])) == [[1, 2, 3], [4, 5, 6], >>> [7, 8, 9], [10]] :param length: length for the returning sequences :param seq: sequence to split """ entries = [] for elem in seq: if len(entries) < length: entries.append(elem) else: yield entries entries = [elem] if entries: yield entries
\ No newline at end of file \ No newline at end of file
......
...@@ -2,7 +2,7 @@ import logging ...@@ -2,7 +2,7 @@ import logging
import unittest import unittest
from satella.coding.sequences import choose, infinite_counter, take_n, is_instance, is_last, \ from satella.coding.sequences import choose, infinite_counter, take_n, is_instance, is_last, \
add_next, half_product, skip_first, zip_shifted, stop_after, group_quantity, \ add_next, half_cartesian, skip_first, zip_shifted, stop_after, group_quantity, \
iter_dict_of_list, shift, other_sequence_no_longer_than, count, even, odd iter_dict_of_list, shift, other_sequence_no_longer_than, count, even, odd
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -53,8 +53,8 @@ class TestSequences(unittest.TestCase): ...@@ -53,8 +53,8 @@ class TestSequences(unittest.TestCase):
b = list(skip_first(a, 1)) b = list(skip_first(a, 1))
self.assertEqual(b, [2, 3, 4, 5]) self.assertEqual(b, [2, 3, 4, 5])
def test_half_product(self): def test_half_cartesian(self):
a = set(half_product([1, 2, 3], [1, 2, 3])) a = set(half_cartesian([1, 2, 3], [1, 2, 3]))
b = set([(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]) b = set([(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)])
self.assertEqual(a, b) self.assertEqual(a, b)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment