Source code for abjad.sequence

import collections.abc
import copy
import itertools
import math
import sys
import typing

import quicktions

from . import enums
from . import math as _math
from .cyclictuple import CyclicTuple
from .ratio import Ratio
from .storage import FormatSpecification, StorageFormatManager


class Sequence(collections.abc.Sequence):
    """
    Sequence.

    ..  container:: example

        Initializes sequence:

        ..  container:: example

            >>> abjad.Sequence([1, 2, 3, 4, 5, 6])
            Sequence([1, 2, 3, 4, 5, 6])

    ..  container:: example

        Initializes and reverses sequence:

        ..  container:: example

            >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6])
            >>> sequence.reverse()
            Sequence([6, 5, 4, 3, 2, 1])

    ..  container:: example

        Initializes, reverses and flattens sequence:

        ..  container:: example

            >>> sequence = abjad.Sequence([1, 2, 3, [4, 5, [6]]])
            >>> sequence = sequence.reverse()
            >>> sequence = sequence.flatten(depth=-1)
            >>> sequence
            Sequence([4, 5, 6, 3, 2, 1])

    ..  container:: example

        REGRESSION:

        >>> abjad.Sequence(0)
        Sequence([0])

    """

    ### CLASS VARIABLES ###

    __slots__ = ("_equivalence_markup", "_expression", "_items")

    ### INITIALIZER ###

    def __init__(self, items=None):
        self._equivalence_markup = None
        self._expression = None
        if items is None:
            items = ()
        if not isinstance(items, collections.abc.Iterable):
            items = [items]
        self._items = tuple(items)

    ### SPECIAL METHODS ###

[docs] def __add__(self, argument) -> "Sequence": r""" Adds ``argument`` to sequence. .. container:: example Adds tuple to sequence: .. container:: example >>> abjad.Sequence([1, 2, 3]) + (4, 5, 6) Sequence([1, 2, 3, 4, 5, 6]) .. container:: example Adds list to sequence: .. container:: example >>> abjad.Sequence([1, 2, 3]) + [4, 5, 6] Sequence([1, 2, 3, 4, 5, 6]) .. container:: example Adds sequence to sequence: .. container:: example >>> sequence_1 = abjad.Sequence([1, 2, 3]) >>> sequence_2 = abjad.Sequence([4, 5, 6]) >>> sequence_1 + sequence_2 Sequence([1, 2, 3, 4, 5, 6]) .. container:: example Reverses result: .. container:: example >>> sequence_1 = abjad.Sequence([1, 2, 3]) >>> sequence_2 = abjad.Sequence([4, 5, 6]) >>> sequence = sequence_1 + sequence_2 >>> sequence.reverse() Sequence([6, 5, 4, 3, 2, 1]) """ argument = type(self)(items=argument) items = self.items + argument.items return type(self)(items)
[docs] def __eq__(self, argument) -> bool: """ Is true when ``argument`` is a sequence with items equal to those of this sequence. .. container:: example Is true when ``argument`` is a sequence with items equal to those of this sequence: >>> abjad.Sequence([1, 2, 3, 4, 5, 6]) == abjad.Sequence([1, 2, 3, 4, 5, 6]) True .. container:: example Is false when ``argument`` is not a sequence with items equal to those of this sequence: >>> abjad.Sequence([1, 2, 3, 4, 5, 6]) == ([1, 2, 3, 4, 5, 6]) False """ return StorageFormatManager.compare_objects(self, argument)
[docs] def __getitem__(self, argument) -> typing.Any: r""" Gets item or slice identified by ``argument``. .. container:: example Gets first item in sequence: .. container:: example >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence[0] 1 .. container:: example Gets last item in sequence: .. container:: example >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence[-1] 6 .. container:: example Gets slice from sequence: .. container:: example >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence = sequence[:3] >>> sequence Sequence([1, 2, 3]) .. container:: example Gets item in sequence and wraps result in new sequence: .. container:: example >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence = abjad.Sequence(sequence[0]) >>> sequence Sequence([1]) .. container:: example Gets slice from sequence and flattens slice: .. container:: example >>> sequence = abjad.Sequence([1, 2, [3, [4]], 5]) >>> sequence = sequence[:-1] >>> sequence = sequence.flatten(depth=-1) >>> sequence Sequence([1, 2, 3, 4]) Returns item or new sequence. """ result = self._items.__getitem__(argument) if isinstance(argument, slice): return type(self)(result) return result
[docs] def __hash__(self) -> int: """ Hashes sequence. """ hash_values = StorageFormatManager(self).get_hash_values() try: result = hash(hash_values) except TypeError: raise TypeError(f"unhashable type: {self}") return result
[docs] def __len__(self) -> int: """ Gets length of sequence. .. container:: example Gets length of sequence: >>> len(abjad.Sequence([1, 2, 3, 4, 5, 6])) 6 .. container:: example Gets length of sequence: >>> len(abjad.Sequence('text')) 4 """ return len(self._items)
[docs] def __radd__(self, argument) -> "Sequence": r""" Adds sequence to ``argument``. .. container:: example Adds sequence to tuple: .. container:: example >>> (1, 2, 3) + abjad.Sequence([4, 5, 6]) Sequence([1, 2, 3, 4, 5, 6]) .. container:: example Adds sequence to list: .. container:: example >>> [1, 2, 3] + abjad.Sequence([4, 5, 6]) Sequence([1, 2, 3, 4, 5, 6]) .. container:: example Adds sequence to sequence: .. container:: example >>> abjad.Sequence([1, 2, 3]) + abjad.Sequence([4, 5, 6]) Sequence([1, 2, 3, 4, 5, 6]) """ argument = type(self)(items=argument) items = argument.items + self.items return type(self)(items)
[docs] def __repr__(self) -> str: """ Gets interpreter representation of sequence. .. container:: example Gets interpreter representation: >>> abjad.Sequence([99]) Sequence([99]) .. container:: example Gets interpreter representation: >>> abjad.Sequence([1, 2, 3, 4, 5, 6]) Sequence([1, 2, 3, 4, 5, 6]) """ items = ", ".join([repr(_) for _ in self.items]) string = f"{type(self).__name__}([{items}])" return string
### PRIVATE METHODS ### # creates an iterator that can generate a flattened list, # descending down into child elements to a depth given in the arguments. # note that depth < 0 is effectively equivalent to infinity. @staticmethod def _flatten_helper(sequence, classes, depth): if not isinstance(sequence, classes): yield sequence elif depth == 0: for item in sequence: yield item else: for item in sequence: # flatten an iterable by one level depth_ = depth - 1 for item_ in Sequence._flatten_helper(item, classes, depth_): yield item_ def _get_format_specification(self): return FormatSpecification(client=self) @classmethod def _partition_sequence_cyclically_by_weights_at_least( class_, sequence, weights, overhang=False ): l_copy = list(sequence) result = [] current_part = [] target_weight_index = 0 len_weights = len(weights) while l_copy: target_weight = weights[target_weight_index % len_weights] item = l_copy.pop(0) current_part.append(item) if target_weight <= _math.weight(current_part): result.append(current_part) current_part = [] target_weight_index += 1 assert not l_copy if current_part: if overhang: result.append(current_part) # return result result = [class_(_) for _ in result] return class_(items=result) @classmethod def _partition_sequence_cyclically_by_weights_at_most( class_, sequence, weights, overhang=False ): result = [] current_part = [] current_target_weight_index = 0 current_target_weight = weights[current_target_weight_index] l_copy = list(sequence) while l_copy: current_target_weight = weights[current_target_weight_index % len(weights)] item = l_copy.pop(0) current_part_weight = _math.weight(current_part) candidate_part_weight = current_part_weight + _math.weight([item]) if candidate_part_weight < current_target_weight: current_part.append(item) elif candidate_part_weight == current_target_weight: current_part.append(item) result.append(current_part) current_part = [] current_target_weight_index += 1 elif current_target_weight < candidate_part_weight: if current_part: l_copy.insert(0, item) result.append(current_part) current_part = [] current_target_weight_index += 1 else: raise Exception("elements in sequence too big.") else: raise ValueError("candidate and target rates must compare.") if current_part: if overhang: result.append(current_part) # return result result = [class_(_) for _ in result] return class_(items=result) @classmethod def _partition_sequence_once_by_weights_at_least( class_, sequence, weights, overhang=False ): result = [] current_part = [] l_copy = list(sequence) for num_weight, target_weight in enumerate(weights): while True: try: item = l_copy.pop(0) except IndexError: if num_weight + 1 == len(weights): if current_part: result.append(current_part) break raise Exception("too few elements in sequence.") current_part.append(item) if target_weight <= _math.weight(current_part): result.append(current_part) current_part = [] break if l_copy: if overhang: result.append(l_copy) result = [class_(_) for _ in result] return class_(items=result) @classmethod def _partition_sequence_once_by_weights_at_most( class_, sequence, weights, overhang=False ): l_copy = list(sequence) result = [] current_part = [] for target_weight in weights: while True: try: item = l_copy.pop(0) except IndexError: raise Exception("too few elements in sequence.") current_weight = _math.weight(current_part) candidate_weight = current_weight + _math.weight([item]) if candidate_weight < target_weight: current_part.append(item) elif candidate_weight == target_weight: current_part.append(item) result.append(current_part) current_part = [] break elif target_weight < candidate_weight: if current_part: result.append(current_part) current_part = [] l_copy.insert(0, item) break else: raise Exception("elements in sequence too big.") else: raise ValueError("candidate and target weights must compare.") if overhang: left_over = current_part + l_copy if left_over: result.append(left_over) result = [class_(_) for _ in result] return class_(items=result) ### PUBLIC PROPERTIES ### @property def items(self) -> typing.Tuple[typing.Any, ...]: """ Gets sequence items. .. container:: example .. container:: example Initializes items positionally: >>> abjad.Sequence([1, 2, 3, 4, 5, 6]).items (1, 2, 3, 4, 5, 6) Initializes items from keyword: >>> abjad.Sequence([1, 2, 3, 4, 5, 6]).items (1, 2, 3, 4, 5, 6) """ return self._items ### PUBLIC METHODS ###
[docs] def filter(self, predicate=None) -> "Sequence": """ Filters sequence by ``predicate``. .. container:: example By length: .. container:: example With lambda: >>> items = [[1], [2, 3, [4]], [5], [6, 7, [8]]] >>> sequence = abjad.Sequence(items) >>> sequence.filter(lambda _: len(_) == 1) Sequence([[1], [5]]) .. container:: example With inequality: >>> items = [[1], [2, 3, [4]], [5], [6, 7, [8]]] >>> sequence = abjad.Sequence(items) >>> sequence.filter(abjad.LengthInequality('==', 1)) Sequence([[1], [5]]) .. container:: example By duration: .. container:: example With inequality: >>> staff = abjad.Staff("c'4. d'8 e'4. f'8 g'2") >>> sequence = abjad.Sequence(staff) >>> sequence.filter(abjad.DurationInequality('==', (1, 8))) Sequence([Note("d'8"), Note("f'8")]) """ if predicate is None: return self[:] items = [] for item in self: if predicate(item): items.append(item) return type(self)(items)
[docs] def flatten(self, classes=None, depth=1) -> "Sequence": r""" Flattens sequence. .. container:: example Flattens sequence: .. container:: example >>> items = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> sequence = abjad.Sequence(items) >>> sequence.flatten() Sequence([1, 2, 3, [4], 5, 6, 7, [8]]) .. container:: example Flattens sequence to depth 2: .. container:: example >>> items = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> sequence = abjad.Sequence(items) >>> sequence.flatten(depth=2) Sequence([1, 2, 3, 4, 5, 6, 7, 8]) .. container:: example Flattens sequence to depth -1: .. container:: example >>> items = [1, [2, 3, [4]], 5, [6, 7, [8]]] >>> sequence = abjad.Sequence(items) >>> sequence.flatten(depth=-1) Sequence([1, 2, 3, 4, 5, 6, 7, 8]) .. container:: example Flattens tuples in sequence only: .. container:: example >>> items = ['ab', 'cd', ('ef', 'gh'), ('ij', 'kl')] >>> sequence = abjad.Sequence(items) >>> sequence.flatten(classes=(tuple,)) Sequence(['ab', 'cd', 'ef', 'gh', 'ij', 'kl']) """ if classes is None: classes = (collections.abc.Sequence,) if Sequence not in classes: classes = tuple(list(classes) + [Sequence]) items = self._flatten_helper(self, classes, depth) return type(self)(items)
[docs] def group_by(self, predicate=None) -> "Sequence": """ Groups sequence items by value of items. .. container:: example >>> items = [0, 0, -1, -1, 2, 3, -5, 1, 1, 5, -5] >>> sequence = abjad.Sequence(items) >>> for item in sequence.group_by(): ... item ... Sequence([0, 0]) Sequence([-1, -1]) Sequence([2]) Sequence([3]) Sequence([-5]) Sequence([1, 1]) Sequence([5]) Sequence([-5]) .. container:: example >>> staff = abjad.Staff("c'8 d' d' e' e' e'") >>> predicate = lambda x: abjad.PitchSet.from_selection(abjad.select(x)) >>> for item in abjad.Sequence(staff).group_by(predicate): ... item ... Sequence([Note("c'8")]) Sequence([Note("d'8"), Note("d'8")]) Sequence([Note("e'8"), Note("e'8"), Note("e'8")]) Returns nested sequence. """ items = [] if predicate is None: pairs = itertools.groupby(self, lambda _: _) for count, group in pairs: item = type(self)(group) items.append(item) else: pairs = itertools.groupby(self, predicate) for count, group in pairs: item = type(self)(group) items.append(item) return type(self)(items)
[docs] def is_decreasing(self, strict=True) -> bool: """ Is true when sequence decreases. .. container:: example Is true when sequence is strictly decreasing: >>> abjad.Sequence([5, 4, 3, 2, 1, 0]).is_decreasing(strict=True) True >>> abjad.Sequence([3, 3, 3, 2, 1, 0]).is_decreasing(strict=True) False >>> abjad.Sequence([3, 3, 3, 3, 3, 3]).is_decreasing(strict=True) False >>> abjad.Sequence().is_decreasing(strict=True) True .. container:: example Is true when sequence decreases monotonically: >>> abjad.Sequence([5, 4, 3, 2, 1, 0]).is_decreasing(strict=False) True >>> abjad.Sequence([3, 3, 3, 2, 1, 0]).is_decreasing(strict=False) True >>> abjad.Sequence([3, 3, 3, 3, 3, 3]).is_decreasing(strict=False) True >>> abjad.Sequence().is_decreasing(strict=False) True """ if strict: try: previous = None for current in self: if previous is not None: if not current < previous: return False previous = current return True except TypeError: return False else: try: previous = None for current in self: if previous is not None: if not current <= previous: return False previous = current return True except TypeError: return False
[docs] def is_increasing(self, strict=True) -> bool: """ Is true when sequence increases. .. container:: example Is true when sequence is strictly increasing: >>> abjad.Sequence([0, 1, 2, 3, 4, 5]).is_increasing(strict=True) True >>> abjad.Sequence([0, 1, 2, 3, 3, 3]).is_increasing(strict=True) False >>> abjad.Sequence([3, 3, 3, 3, 3, 3]).is_increasing(strict=True) False >>> abjad.Sequence().is_increasing(strict=True) True .. container:: example Is true when sequence increases monotonically: >>> abjad.Sequence([0, 1, 2, 3, 4, 5]).is_increasing(strict=False) True >>> abjad.Sequence([0, 1, 2, 3, 3, 3]).is_increasing(strict=False) True >>> abjad.Sequence([3, 3, 3, 3, 3, 3]).is_increasing(strict=False) True >>> abjad.Sequence().is_increasing(strict=False) True """ if strict: try: previous = None for current in self: if previous is not None: if not previous < current: return False previous = current return True except TypeError: return False else: try: previous = None for current in self: if previous is not None: if not previous <= current: return False previous = current return True except TypeError: return False
[docs] def is_permutation(self, length=None) -> bool: """ Is true when sequence is a permutation. .. container:: example Is true when sequence is a permutation: >>> abjad.Sequence([4, 5, 0, 3, 2, 1]).is_permutation() True .. container:: example Is false when sequence is not a permutation: >>> abjad.Sequence([1, 1, 5, 3, 2, 1]).is_permutation() False """ return tuple(sorted(self)) == tuple(range(len(self)))
[docs] def is_repetition_free(self) -> bool: """ Is true when sequence is repetition-free. .. container:: example Is true when sequence is repetition-free: >>> abjad.Sequence([0, 1, 2, 6, 7, 8]).is_repetition_free() True .. container:: example Is true when sequence is empty: >>> abjad.Sequence().is_repetition_free() True .. container:: example Is false when sequence contains repetitions: >>> abjad.Sequence([0, 1, 2, 2, 7, 8]).is_repetition_free() False """ try: for left, right in self.nwise(): if left == right: return False return True except TypeError: return False
[docs] def join(self) -> "Sequence": r""" Join subsequences in ``sequence``. .. container:: example >>> items = [(1, 2, 3), (), (4, 5), (), (6,)] >>> sequence = abjad.Sequence(items) >>> sequence Sequence([(1, 2, 3), (), (4, 5), (), (6,)]) >>> sequence.join() Sequence([(1, 2, 3, 4, 5, 6)]) """ if not self: return type(self)() item = self[0] for item_ in self[1:]: item += item_ return type(self)([item])
[docs] def map(self, operand=None) -> "Sequence": r""" Maps ``operand`` to sequence items. .. container:: example Partitions sequence and sums parts: .. container:: example >>> sequence = abjad.Sequence(range(1, 10+1)) >>> sequence = sequence.partition_by_counts( ... [3], ... cyclic=True, ... ) >>> sequence = sequence.map(sum) >>> sequence Sequence([6, 15, 24]) .. container:: example Maps identity: >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence.map() Sequence([1, 2, 3, 4, 5, 6]) """ if operand is not None: items = [] for i, item_ in enumerate(self): item_ = operand(item_) items.append(item_) else: items = list(self.items[:]) return type(self)(items)
[docs] def nwise(self, n=2, cyclic=False, wrapped=False) -> typing.Generator: """ Iterates sequence ``n`` at a time. .. container:: example Iterates iterable by pairs: >>> sequence = abjad.Sequence(range(10)) >>> for item in sequence.nwise(): ... item ... Sequence([0, 1]) Sequence([1, 2]) Sequence([2, 3]) Sequence([3, 4]) Sequence([4, 5]) Sequence([5, 6]) Sequence([6, 7]) Sequence([7, 8]) Sequence([8, 9]) .. container:: example Iterates iterable by triples: >>> sequence = abjad.Sequence(range(10)) >>> for item in sequence.nwise(n=3): ... item ... Sequence([0, 1, 2]) Sequence([1, 2, 3]) Sequence([2, 3, 4]) Sequence([3, 4, 5]) Sequence([4, 5, 6]) Sequence([5, 6, 7]) Sequence([6, 7, 8]) Sequence([7, 8, 9]) .. container:: example Iterates iterable by pairs. Wraps around at end: >>> sequence = abjad.Sequence(range(10)) >>> for item in sequence.nwise(n=2, wrapped=True): ... item ... Sequence([0, 1]) Sequence([1, 2]) Sequence([2, 3]) Sequence([3, 4]) Sequence([4, 5]) Sequence([5, 6]) Sequence([6, 7]) Sequence([7, 8]) Sequence([8, 9]) Sequence([9, 0]) .. container:: example Iterates iterable by triples. Wraps around at end: >>> sequence = abjad.Sequence(range(10)) >>> for item in sequence.nwise(n=3, wrapped=True): ... item ... Sequence([0, 1, 2]) Sequence([1, 2, 3]) Sequence([2, 3, 4]) Sequence([3, 4, 5]) Sequence([4, 5, 6]) Sequence([5, 6, 7]) Sequence([6, 7, 8]) Sequence([7, 8, 9]) Sequence([8, 9, 0]) Sequence([9, 0, 1]) .. container:: example Iterates iterable by pairs. Cycles indefinitely: >>> sequence = abjad.Sequence(range(10)) >>> pairs = sequence.nwise(n=2, cyclic=True) >>> for _ in range(15): ... next(pairs) ... Sequence([0, 1]) Sequence([1, 2]) Sequence([2, 3]) Sequence([3, 4]) Sequence([4, 5]) Sequence([5, 6]) Sequence([6, 7]) Sequence([7, 8]) Sequence([8, 9]) Sequence([9, 0]) Sequence([0, 1]) Sequence([1, 2]) Sequence([2, 3]) Sequence([3, 4]) Sequence([4, 5]) Returns infinite generator. .. container:: example Iterates iterable by triples. Cycles indefinitely: >>> sequence = abjad.Sequence(range(10)) >>> triples = sequence.nwise(n=3, cyclic=True) >>> for _ in range(15): ... next(triples) ... Sequence([0, 1, 2]) Sequence([1, 2, 3]) Sequence([2, 3, 4]) Sequence([3, 4, 5]) Sequence([4, 5, 6]) Sequence([5, 6, 7]) Sequence([6, 7, 8]) Sequence([7, 8, 9]) Sequence([8, 9, 0]) Sequence([9, 0, 1]) Sequence([0, 1, 2]) Sequence([1, 2, 3]) Sequence([2, 3, 4]) Sequence([3, 4, 5]) Sequence([4, 5, 6]) Returns infinite generator. .. container:: example Iterates items one at a time: >>> sequence = abjad.Sequence(range(10)) >>> for item in sequence.nwise(n=1): ... item ... Sequence([0]) Sequence([1]) Sequence([2]) Sequence([3]) Sequence([4]) Sequence([5]) Sequence([6]) Sequence([7]) Sequence([8]) Sequence([9]) Ignores ``wrapped`` when ``cyclic`` is true. """ if cyclic: item_buffer = [] long_enough = False for item in self: item_buffer.append(item) if not long_enough: if n <= len(item_buffer): long_enough = True if long_enough: yield type(self)(item_buffer[-n:]) len_sequence = len(item_buffer) current = len_sequence - n + 1 while True: output = [] for local_offset in range(n): index = (current + local_offset) % len_sequence output.append(item_buffer[index]) yield type(self)(output) current += 1 current %= len_sequence elif wrapped: first_n_minus_1: typing.List[typing.Any] = [] item_buffer = [] for item in self: item_buffer.append(item) if len(item_buffer) == n: yield type(self)(item_buffer) item_buffer.pop(0) if len(first_n_minus_1) < n - 1: first_n_minus_1.append(item) item_buffer = item_buffer + first_n_minus_1 if item_buffer: for x in range(n - 1): stop = x + n yield type(self)(item_buffer[x:stop]) else: item_buffer = [] for item in self: item_buffer.append(item) if len(item_buffer) == n: yield type(self)(item_buffer) item_buffer.pop(0)
[docs] def partition_by_counts( self, counts, cyclic=False, enchain=False, overhang=False, reversed_=False, ) -> "Sequence": r""" Partitions sequence by ``counts``. .. container:: example Partitions sequence once by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> sequence = sequence.partition_by_counts( ... [3], ... cyclic=False, ... overhang=False, ... ) >>> sequence Sequence([Sequence([0, 1, 2])]) >>> for part in sequence: ... part Sequence([0, 1, 2]) .. container:: example Partitions sequence once by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=False, ... overhang=False, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3]) Sequence([4, 5, 6]) .. container:: example Partitions sequence cyclically by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=True, ... overhang=False, ... ) >>> for part in parts: ... part Sequence([0, 1, 2]) Sequence([3, 4, 5]) Sequence([6, 7, 8]) Sequence([9, 10, 11]) Sequence([12, 13, 14]) .. container:: example Partitions sequence cyclically by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=True, ... overhang=False, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3]) Sequence([4, 5, 6]) Sequence([7, 8, 9, 10]) Sequence([11, 12, 13]) .. container:: example Partitions sequence once by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2]) Sequence([3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) .. container:: example Partitions sequence once by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3]) Sequence([4, 5, 6]) Sequence([7, 8, 9, 10, 11, 12, 13, 14, 15]) .. container:: example Partitions sequence cyclically by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=True, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2]) Sequence([3, 4, 5]) Sequence([6, 7, 8]) Sequence([9, 10, 11]) Sequence([12, 13, 14]) Sequence([15]) .. container:: example Partitions sequence cyclically by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=True, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3]) Sequence([4, 5, 6]) Sequence([7, 8, 9, 10]) Sequence([11, 12, 13]) Sequence([14, 15]) .. container:: example Reverse-partitions sequence once by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=False, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([13, 14, 15]) .. container:: example Reverse-partitions sequence once by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=False, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([9, 10, 11]) Sequence([12, 13, 14, 15]) .. container:: example Reverse-partitions sequence cyclically by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=True, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([1, 2, 3]) Sequence([4, 5, 6]) Sequence([7, 8, 9]) Sequence([10, 11, 12]) Sequence([13, 14, 15]) .. container:: example Reverse-partitions sequence cyclically by counts without overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=True, ... overhang=False, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([2, 3, 4]) Sequence([5, 6, 7, 8]) Sequence([9, 10, 11]) Sequence([12, 13, 14, 15]) .. container:: example Reverse-partitions sequence once by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=False, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]) Sequence([13, 14, 15]) .. container:: example Reverse-partitions sequence once by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=False, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8]) Sequence([9, 10, 11]) Sequence([12, 13, 14, 15]) .. container:: example Reverse-partitions sequence cyclically by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=True, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([0]) Sequence([1, 2, 3]) Sequence([4, 5, 6]) Sequence([7, 8, 9]) Sequence([10, 11, 12]) Sequence([13, 14, 15]) .. container:: example Reverse-partitions sequence cyclically by counts with overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [4, 3], ... cyclic=True, ... overhang=True, ... reversed_=True, ... ) >>> for part in parts: ... part Sequence([0, 1]) Sequence([2, 3, 4]) Sequence([5, 6, 7, 8]) Sequence([9, 10, 11]) Sequence([12, 13, 14, 15]) .. container:: example Partitions sequence once by counts and asserts that sequence partitions exactly (with no overhang): .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> parts = sequence.partition_by_counts( ... [2, 3, 5], ... cyclic=False, ... overhang=abjad.Exact, ... ) >>> for part in parts: ... part Sequence([0, 1]) Sequence([2, 3, 4]) Sequence([5, 6, 7, 8, 9]) .. container:: example Partitions sequence cyclically by counts and asserts that sequence partitions exactly Exact partitioning means partitioning with no overhang: .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> parts = sequence.partition_by_counts( ... [2], ... cyclic=True, ... overhang=abjad.Exact, ... ) >>> for part in parts: ... part Sequence([0, 1]) Sequence([2, 3]) Sequence([4, 5]) Sequence([6, 7]) Sequence([8, 9]) .. container:: example Partitions string: .. container:: example >>> sequence = abjad.Sequence('some text') >>> parts = sequence.partition_by_counts( ... [3], ... cyclic=False, ... overhang=True, ... ) >>> for part in parts: ... part Sequence(['s', 'o', 'm']) Sequence(['e', ' ', 't', 'e', 'x', 't']) .. container:: example Partitions sequence cyclically into enchained parts by counts; truncates overhang: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [2, 6], ... cyclic=True, ... enchain=True, ... overhang=False, ... ) >>> for part in parts: ... part Sequence([0, 1]) Sequence([1, 2, 3, 4, 5, 6]) Sequence([6, 7]) Sequence([7, 8, 9, 10, 11, 12]) Sequence([12, 13]) .. container:: example Partitions sequence cyclically into enchained parts by counts; returns overhang at end: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [2, 6], ... cyclic=True, ... enchain=True, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1]) Sequence([1, 2, 3, 4, 5, 6]) Sequence([6, 7]) Sequence([7, 8, 9, 10, 11, 12]) Sequence([12, 13]) Sequence([13, 14, 15]) .. container:: example REGRESSION: partitions sequence cyclically into enchained parts by counts; does not return false 1-element part at end: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts( ... [5], ... cyclic=True, ... enchain=True, ... overhang=True, ... ) >>> for part in parts: ... part Sequence([0, 1, 2, 3, 4]) Sequence([4, 5, 6, 7, 8]) Sequence([8, 9, 10, 11, 12]) Sequence([12, 13, 14, 15]) .. container:: example Edge case: empty counts nests sequence and ignores keywords: .. container:: example >>> sequence = abjad.Sequence(range(16)) >>> parts = sequence.partition_by_counts([]) >>> for part in parts: ... part Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) Returns nested sequence. """ if not all(isinstance(_, int) and 0 <= _ for _ in counts): raise Exception(f"must be nonnegative integers: {counts!r}.") sequence = self if reversed_: sequence = type(self)(reversed(sequence)) if counts: counts = CyclicTuple(counts) else: return type(self)([sequence]) result = [] i, start = 0, 0 while True: count = counts[i] stop = start + count part = sequence[start:stop] if len(sequence) < stop: if enchain and len(part) == 1: part = None break result.append(part) start = stop i += 1 if not cyclic and len(counts) <= i: part = sequence[start:] break if enchain: start -= 1 if part: if overhang is True: result.append(part) elif overhang is enums.Exact and len(part) == count: result.append(part) elif overhang is enums.Exact and len(part) != count: raise Exception("sequence does not partition exactly.") if reversed_: result_ = [] for part in reversed(result): part_type = type(part) part = reversed(part) part = part_type(part) result_.append(part) result = result_ return type(self)(result)
[docs] def partition_by_ratio_of_lengths(self, ratio) -> "Sequence": r""" Partitions sequence by ``ratio`` of lengths. .. container:: example Partitions sequence by ``1:1:1`` ratio: .. container:: example >>> numbers = abjad.Sequence(range(10)) >>> ratio = abjad.Ratio((1, 1, 1)) >>> for part in numbers.partition_by_ratio_of_lengths(ratio): ... part Sequence([0, 1, 2]) Sequence([3, 4, 5, 6]) Sequence([7, 8, 9]) .. container:: example Partitions sequence by ``1:1:2`` ratio: .. container:: example >>> numbers = abjad.Sequence(range(10)) >>> ratio = abjad.Ratio((1, 1, 2)) >>> for part in numbers.partition_by_ratio_of_lengths(ratio): ... part Sequence([0, 1, 2]) Sequence([3, 4]) Sequence([5, 6, 7, 8, 9]) Returns nested sequence. """ ratio = Ratio(ratio) length = len(self) counts = ratio.partition_integer(length) parts = self.partition_by_counts(counts, cyclic=False, overhang=enums.Exact) return type(self)(parts)
[docs] def partition_by_ratio_of_weights(self, weights) -> "Sequence": """ Partitions sequence by ratio of ``weights``. .. container:: example >>> ratio = abjad.Ratio([1, 1, 1]) >>> sequence = abjad.Sequence(10 * [1]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1]) Sequence([1, 1, 1, 1]) Sequence([1, 1, 1]) .. container:: example >>> ratio = abjad.Ratio([1, 1, 1, 1]) >>> sequence = abjad.Sequence(10 * [1]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1]) Sequence([1, 1]) Sequence([1, 1, 1]) Sequence([1, 1]) .. container:: example >>> ratio = abjad.Ratio([2, 2, 3]) >>> sequence = abjad.Sequence(10 * [1]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1]) Sequence([1, 1, 1]) Sequence([1, 1, 1, 1]) .. container:: example >>> ratio = abjad.Ratio([3, 2, 2]) >>> sequence = abjad.Sequence(10 * [1]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1, 1]) Sequence([1, 1, 1]) Sequence([1, 1, 1]) .. container:: example >>> ratio = abjad.Ratio([1, 1]) >>> items = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2] >>> sequence = abjad.Sequence(items) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1, 1, 1, 1, 2, 2]) Sequence([2, 2, 2, 2]) .. container:: example >>> ratio = abjad.Ratio([1, 1, 1]) >>> items = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2] >>> sequence = abjad.Sequence(items) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([1, 1, 1, 1, 1, 1]) Sequence([2, 2, 2]) Sequence([2, 2, 2]) .. container:: example >>> ratio = abjad.Ratio([1, 1, 1]) >>> sequence = abjad.Sequence([5, 5]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([5]) Sequence([5]) Sequence([]) .. container:: example >>> ratio = abjad.Ratio([1, 1, 1, 1]) >>> sequence = abjad.Sequence([5, 5]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([5]) Sequence([]) Sequence([5]) Sequence([]) .. container:: example >>> ratio = abjad.Ratio([2, 2, 3]) >>> sequence = abjad.Sequence([5, 5]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([5]) Sequence([5]) Sequence([]) .. container:: example >>> ratio = abjad.Ratio([3, 2, 2]) >>> sequence = abjad.Sequence([5, 5]) >>> sequence = sequence.partition_by_ratio_of_weights(ratio) >>> for item in sequence: ... item ... Sequence([5]) Sequence([5]) Sequence([]) Rounded weight-proportions of sequences returned equal to rounded ``weights``. Returns nested sequence. """ list_weight = _math.weight(self) weights_parts = Ratio(weights).partition_integer(list_weight) cumulative_weights = _math.cumulative_sums(weights_parts, start=None) items = [] sublist: typing.List[typing.Any] = [] items.append(sublist) current_cumulative_weight = cumulative_weights.pop(0) for item in self: if not isinstance(item, (int, float, quicktions.Fraction)): raise TypeError(f"must be number: {item!r}.") sublist.append(item) while current_cumulative_weight <= _math.weight( type(self)(items).flatten(depth=-1) ): try: current_cumulative_weight = cumulative_weights.pop(0) sublist = [] items.append(sublist) except IndexError: break items_ = [type(self)(_) for _ in items] return type(self)(items_)
[docs] def partition_by_weights( self, weights, # TODO: make keyword-only: cyclic=False, overhang=False, allow_part_weights=enums.Exact, ) -> "Sequence": r""" Partitions sequence by ``weights`` exactly. >>> sequence = abjad.Sequence([3, 3, 3, 3, 4, 4, 4, 4, 5]) .. container:: example Partitions sequence once by weights with overhang: >>> for item in sequence.partition_by_weights( ... [3, 9], ... cyclic=False, ... overhang=False, ... ): ... item ... Sequence([3]) Sequence([3, 3, 3]) .. container:: example Partitions sequence once by weights. Allows overhang: >>> for item in sequence.partition_by_weights( ... [3, 9], ... cyclic=False, ... overhang=True, ... ): ... item ... Sequence([3]) Sequence([3, 3, 3]) Sequence([4, 4, 4, 4, 5]) .. container:: example Partitions sequence cyclically by weights: >>> for item in sequence.partition_by_weights( ... [12], ... cyclic=True, ... overhang=False, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4, 4, 4]) .. container:: example Partitions sequence cyclically by weights. Allows overhang: >>> for item in sequence.partition_by_weights( ... [12], ... cyclic=True, ... overhang=True, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4, 4, 4]) Sequence([4, 5]) >>> sequence = abjad.Sequence([3, 3, 3, 3, 4, 4, 4, 4, 5, 5]) .. container:: example Partitions sequence once by weights. Allows part weights to be just less than specified: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=False, ... overhang=False, ... allow_part_weights=abjad.Less, ... ): ... item ... Sequence([3, 3, 3]) Sequence([3]) .. container:: example Partitions sequence once by weights. Allows part weights to be just less than specified. Allows overhang: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=False, ... overhang=True, ... allow_part_weights=abjad.Less, ... ): ... item ... Sequence([3, 3, 3]) Sequence([3]) Sequence([4, 4, 4, 4, 5, 5]) .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just less than specified: >>> for item in sequence.partition_by_weights( ... [10, 5], ... cyclic=True, ... overhang=False, ... allow_part_weights=abjad.Less, ... ): ... item ... Sequence([3, 3, 3]) Sequence([3]) Sequence([4, 4]) Sequence([4]) Sequence([4, 5]) Sequence([5]) .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just less than specified. Allows overhang: >>> for item in sequence.partition_by_weights( ... [10, 5], ... cyclic=True, ... overhang=True, ... allow_part_weights=abjad.Less, ... ): ... item ... Sequence([3, 3, 3]) Sequence([3]) Sequence([4, 4]) Sequence([4]) Sequence([4, 5]) Sequence([5]) >>> sequence = abjad.Sequence([3, 3, 3, 3, 4, 4, 4, 4, 5, 5]) .. container:: example Partitions sequence once by weights. Allow part weights to be just more than specified: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=False, ... overhang=False, ... allow_part_weights=abjad.More, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4]) .. container:: example Partitions sequence once by weights. Allows part weights to be just more than specified. Allows overhang: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=False, ... overhang=True, ... allow_part_weights=abjad.More, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4]) Sequence([4, 4, 4, 5, 5]) .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just more than specified: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=True, ... overhang=False, ... allow_part_weights=abjad.More, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4]) Sequence([4, 4, 4]) Sequence([5]) .. container:: example Partitions sequence cyclically by weights. Allows part weights to be just more than specified. Allows overhang: >>> for item in sequence.partition_by_weights( ... [10, 4], ... cyclic=True, ... overhang=True, ... allow_part_weights=abjad.More, ... ): ... item ... Sequence([3, 3, 3, 3]) Sequence([4]) Sequence([4, 4, 4]) Sequence([5]) Sequence([5]) Returns nested sequence. """ if allow_part_weights is enums.Exact: candidate = type(self)(self) candidate = candidate.split(weights, cyclic=cyclic, overhang=overhang) flattened_candidate = candidate.flatten(depth=-1) if flattened_candidate == self[: len(flattened_candidate)]: return candidate else: raise Exception("can not partition exactly.") elif allow_part_weights is enums.More: if not cyclic: return Sequence._partition_sequence_once_by_weights_at_least( self, weights, overhang=overhang ) else: return Sequence._partition_sequence_cyclically_by_weights_at_least( self, weights, overhang=overhang ) elif allow_part_weights is enums.Less: if not cyclic: return Sequence._partition_sequence_once_by_weights_at_most( self, weights, overhang=overhang ) else: return Sequence._partition_sequence_cyclically_by_weights_at_most( self, weights, overhang=overhang ) else: message = "allow_part_weights must be ordinal constant: {!r}." message = message.format(allow_part_weights) raise ValueError(message)
[docs] def permute(self, permutation) -> "Sequence": r""" Permutes sequence by ``permutation``. .. container:: example >>> sequence = abjad.Sequence([10, 11, 12, 13, 14, 15]) >>> sequence.permute([5, 4, 0, 1, 2, 3]) Sequence([15, 14, 10, 11, 12, 13]) .. container:: example >>> sequence = abjad.Sequence([11, 12, 13, 14]) >>> sequence.permute([1, 0, 3, 2]) Sequence([12, 11, 14, 13]) .. container:: example Raises exception when lengths do not match: >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6]) >>> sequence.permute([3, 0, 1, 2]) Traceback (most recent call last): ... ValueError: permutation Sequence([3, 0, 1, 2]) must match length of Sequence([1, 2, 3, 4, 5, 6]). """ permutation = type(self)(permutation) if not permutation.is_permutation(): raise ValueError(f"must be permutation: {permutation!r}.") if len(permutation) != len(self): message = f"permutation {permutation!r} must match length of {self !r}." raise ValueError(message) result = [] for i, item in enumerate(self): j = permutation[i] item_ = self[j] result.append(item_) return type(self)(result)
# TODO: change input to pattern
[docs] def remove(self, indices=None, period=None) -> "Sequence": """ Removes items at ``indices``. .. container:: example >>> sequence = abjad.Sequence(range(15)) .. container:: example >>> sequence.remove() Sequence([]) .. container:: example >>> sequence.remove(indices=[2, 3]) Sequence([0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) .. container:: example Removes elements and indices -2 and -3: >>> sequence.remove(indices=[-2, -3]) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14]) .. container:: example >>> sequence.remove(indices=[2, 3], period=4) Sequence([0, 1, 4, 5, 8, 9, 12, 13]) .. container:: example >>> sequence.remove(indices=[-2, -3], period=4) Sequence([2, 3, 6, 7, 10, 11, 14]) .. container:: example >>> sequence.remove(indices=[]) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) .. container:: example >>> sequence.remove(indices=[97, 98, 99]) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) .. container:: example Removes no elements: >>> sequence.remove(indices=[-97, -98, -99]) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) """ items = [] length = len(self) period = period or length if indices is None: indices = range(length) new_indices = [] for i in indices: if length < abs(i): continue if i < 0: i = length + i i = i % period new_indices.append(i) indices = new_indices indices.sort() for i, item in enumerate(self): if i % period not in indices: items.append(item) return type(self)(items)
[docs] def remove_repeats(self) -> "Sequence": """ Removes repeats from ``sequence``. .. container:: example >>> items = [31, 31, 35, 35, 31, 31, 31, 31, 35] >>> sequence = abjad.Sequence(items) >>> sequence.remove_repeats() Sequence([31, 35, 31, 35]) """ items = [self[0]] for item in self[1:]: if item != items[-1]: items.append(item) return type(self)(items)
[docs] def repeat(self, n=1) -> "Sequence": r""" Repeats sequence. .. container:: example .. container:: example >>> abjad.Sequence([1, 2, 3]).repeat(n=0) Sequence([]) .. container:: example .. container:: example >>> abjad.Sequence([1, 2, 3]).repeat(n=1) Sequence([Sequence([1, 2, 3])]) .. container:: example .. container:: example >>> abjad.Sequence([1, 2, 3]).repeat(n=2) Sequence([Sequence([1, 2, 3]), Sequence([1, 2, 3])]) Returns nested sequence. """ items = [] for i in range(n): items.append(self[:]) return type(self)(items)
[docs] def repeat_to_length(self, length=None, start=0) -> "Sequence": """ Repeats sequence to ``length``. .. container:: example Repeats list to length 11: >>> abjad.Sequence(range(5)).repeat_to_length(11) Sequence([0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0]) .. container:: example >>> abjad.Sequence(range(5)).repeat_to_length(11, start=2) Sequence([2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2]) .. container:: example >>> sequence = abjad.Sequence([0, -1, -2, -3, -4]) >>> sequence.repeat_to_length(11) Sequence([0, -1, -2, -3, -4, 0, -1, -2, -3, -4, 0]) .. container:: example >>> sequence.repeat_to_length(0) Sequence([]) .. container:: example >>> abjad.Sequence([1, 2, 3]).repeat_to_length(10, start=100) Sequence([2, 3, 1, 2, 3, 1, 2, 3, 1, 2]) """ assert _math.is_nonnegative_integer(length), repr(length) assert len(self), repr(self) items = [] start %= len(self) stop_index = start + length repetitions = int(math.ceil(float(stop_index) / len(self))) for i in range(repetitions): for item in self: items.append(item) return type(self)(items[start:stop_index])
[docs] def repeat_to_weight(self, weight, allow_total=enums.Exact) -> "Sequence": """ Repeats sequence to ``weight``. .. container:: example Repeats sequence to weight of 23 exactly: >>> abjad.Sequence([5, -5, -5]).repeat_to_weight(23) Sequence([5, -5, -5, 5, -3]) .. container:: example Repeats sequence to weight of 23 more: >>> sequence = abjad.Sequence([5, -5, -5]) >>> sequence.repeat_to_weight(23, allow_total=abjad.More) Sequence([5, -5, -5, 5, -5]) .. container:: example Repeats sequence to weight of 23 or less: >>> sequence = abjad.Sequence([5, -5, -5]) >>> sequence.repeat_to_weight(23, allow_total=abjad.Less) Sequence([5, -5, -5, 5]) .. container:: example >>> items = [abjad.NonreducedFraction(3, 16)] >>> sequence = abjad.Sequence(items) >>> weight = abjad.NonreducedFraction(5, 4) >>> sequence = sequence.repeat_to_weight(weight) >>> sum(sequence) NonreducedFraction(20, 16) >>> [_.pair for _ in sequence] [(3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (2, 16)] """ assert 0 <= weight if allow_total is enums.Exact: sequence_weight = _math.weight(self) complete_repetitions = int( math.ceil(float(weight) / float(sequence_weight)) ) items = list(self) items = complete_repetitions * items overage = complete_repetitions * sequence_weight - weight for item in reversed(items): if 0 < overage: element_weight = abs(item) candidate_overage = overage - element_weight if 0 <= candidate_overage: overage = candidate_overage items.pop() else: absolute_amount_to_keep = element_weight - overage assert 0 < absolute_amount_to_keep signed_amount_to_keep = absolute_amount_to_keep signed_amount_to_keep *= _math.sign(item) items.pop() items.append(signed_amount_to_keep) break else: break elif allow_total is enums.Less: items = [self[0]] i = 1 while _math.weight(items) < weight: items.append(self[i % len(self)]) i += 1 if weight < _math.weight(items): items = items[:-1] return type(self)(items) elif allow_total is enums.More: items = [self[0]] i = 1 while _math.weight(items) < weight: items.append(self[i % len(self)]) i += 1 return type(self)(items) else: raise ValueError(f"is not an ordinal value constant: {allow_total!r}.") return type(self)(items=items)
[docs] def replace(self, old, new) -> "Sequence": """ Replaces ``old`` with ``new``. .. container:: example >>> sequence = abjad.Sequence([0, 2, 3, 0, 2, 3, 0, 2, 3]) >>> sequence.replace(0, 1) Sequence([1, 2, 3, 1, 2, 3, 1, 2, 3]) """ items = [] for item in self: if item == old: new_copy = copy.copy(new) items.append(new_copy) else: items.append(item) return type(self)(items=items)
[docs] def replace_at(self, indices, new_material) -> "Sequence": """ Replaces items at ``indices`` with ``new_material``. .. container:: example Replaces items at indices 0, 2, 4, 6: >>> sequence = abjad.Sequence(range(16)) >>> sequence.replace_at( ... ([0], 2), ... (['A', 'B', 'C', 'D'], None), ... ) Sequence(['A', 1, 'B', 3, 'C', 5, 'D', 7, 8, 9, 10, 11, 12, 13, 14, 15]) .. container:: example Replaces elements at indices 0, 1, 8, 13: >>> sequence = abjad.Sequence(range(16)) >>> sequence.replace_at( ... ([0, 1, 8, 13], None), ... (['A', 'B', 'C', 'D'], None), ... ) Sequence(['A', 'B', 2, 3, 4, 5, 6, 7, 'C', 9, 10, 11, 12, 'D', 14, 15]) .. container:: example Replaces every item at even index: >>> sequence = abjad.Sequence(range(16)) >>> sequence.replace_at( ... ([0], 2), ... (['*'], 1), ... ) Sequence(['*', 1, '*', 3, '*', 5, '*', 7, '*', 9, '*', 11, '*', 13, '*', 15]) .. container:: example Replaces every element at an index congruent to 0 (mod 6) with ``'A'``; replaces every element at an index congruent to 2 (mod 6) with ``'B'``: >>> sequence = abjad.Sequence(range(16)) >>> sequence.replace_at( ... ([0], 2), ... (['A', 'B'], 3), ... ) Sequence(['A', 1, 'B', 3, 4, 5, 'A', 7, 'B', 9, 10, 11, 'A', 13, 'B', 15]) """ assert isinstance(indices, collections.abc.Sequence) assert len(indices) == 2 index_values, index_period = indices assert isinstance(index_values, collections.abc.Sequence) index_values = list(index_values) assert isinstance(index_period, (int, type(None))) assert isinstance(new_material, collections.abc.Sequence) assert len(new_material) == 2 material_values, material_period = new_material assert isinstance(material_values, collections.abc.Sequence) material_values = list(material_values) assert isinstance(material_period, (int, type(None))) maxsize = sys.maxsize if index_period is None: index_period = maxsize if material_period is None: material_period = maxsize items = [] material_index = 0 for index, item in enumerate(self): if index % index_period in index_values: try: cyclic_material_index = material_index % material_period material_value = material_values[cyclic_material_index] items.append(material_value) except IndexError: items.append(item) material_index += 1 else: items.append(item) return type(self)(items=items)
# TODO: remove in favor of self.retain_pattern()
[docs] def retain(self, indices=None, period=None) -> "Sequence": """ Retains items at ``indices``. .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> sequence.retain() Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) .. container:: example >>> sequence.retain(indices=[2, 3]) Sequence([2, 3]) .. container:: example >>> sequence.retain(indices=[-2, -3]) Sequence([7, 8]) .. container:: example >>> sequence.retain(indices=[2, 3], period=4) Sequence([2, 3, 6, 7]) .. container:: example >>> sequence.retain(indices=[-2, -3], period=4) Sequence([0, 3, 4, 7, 8]) .. container:: example >>> sequence.retain(indices=[]) Sequence([]) .. container:: example >>> sequence.retain(indices=[97, 98, 99]) Sequence([]) .. container:: example >>> sequence.retain(indices=[-97, -98, -99]) Sequence([]) """ length = len(self) period = period or length if indices is None: indices = range(length) new_indices = [] for i in indices: if length < abs(i): continue if i < 0: i = length + i i = i % period new_indices.append(i) indices = new_indices indices.sort() items = [] for i, item in enumerate(self): if i % period in indices: items.append(item) return type(self)(items=items)
[docs] def retain_pattern(self, pattern) -> "Sequence": """ Retains items at indices matching ``pattern``. .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> sequence.retain_pattern(abjad.index_all()) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) .. container:: example >>> sequence.retain_pattern(abjad.index([2, 3])) Sequence([2, 3]) .. container:: example >>> sequence.retain_pattern(abjad.index([-2, -3])) Sequence([7, 8]) .. container:: example >>> sequence.retain_pattern(abjad.index([2, 3], 4)) Sequence([2, 3, 6, 7]) .. container:: example >>> sequence.retain_pattern(abjad.index([-2, -3], 4)) Sequence([0, 3, 4, 7, 8]) .. container:: example >>> sequence.retain_pattern(abjad.index([97, 98, 99])) Sequence([]) .. container:: example >>> sequence.retain_pattern(abjad.index([-97, -98, -99])) Sequence([]) """ length = len(self) items = [] for i, item in enumerate(self): if pattern.matches_index(i, length): items.append(item) return type(self)(items=items)
[docs] def reverse(self, recurse=False) -> "Sequence": r""" Reverses sequence. .. container:: example Reverses sequence: .. container:: example >>> sequence = abjad.Sequence([[1, 2], 3, [4, 5]]) >>> sequence.reverse() Sequence([[4, 5], 3, [1, 2]]) .. container:: example Reverses recursively: .. container:: example >>> segment_1 = abjad.PitchClassSegment([1, 2]) >>> pitch = abjad.NumberedPitch(3) >>> segment_2 = abjad.PitchClassSegment([4, 5]) >>> sequence = abjad.Sequence([segment_1, pitch, segment_2]) >>> for item in sequence.reverse(recurse=True): ... item ... PitchClassSegment([5, 4]) NumberedPitch(3) PitchClassSegment([2, 1]) """ if not recurse: return type(self)(items=reversed(self)) def _reverse_helper(item): if isinstance(item, collections.abc.Iterable): subitems_ = [_reverse_helper(_) for _ in reversed(item)] return type(item)(subitems_) else: return item items = _reverse_helper(self.items) return type(self)(items=items)
def rotate(self, n=0) -> "Sequence": r""" Rotates sequence by index ``n``. .. container:: example Rotates sequence to the right: .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> sequence.rotate(n=4) Sequence([6, 7, 8, 9, 0, 1, 2, 3, 4, 5]) .. container:: example Rotates sequence to the left: .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> sequence.rotate(n=-3) Sequence([3, 4, 5, 6, 7, 8, 9, 0, 1, 2]) .. container:: example Rotates sequence neither to the right nor the left: .. container:: example >>> sequence = abjad.Sequence(range(10)) >>> sequence.rotate(n=0) Sequence([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) """ n = n or 0 items = [] if len(self): n = n % len(self) for item in self[-n : len(self)] + self[:-n]: items.append(item) return type(self)(items=items)
[docs] def sort(self, key=None, reverse=False) -> "Sequence": """ Sorts sequence. .. container:: example >>> sequence = abjad.Sequence([3, 2, 5, 4, 1, 6]) >>> sequence.sort() Sequence([1, 2, 3, 4, 5, 6]) >>> sequence Sequence([3, 2, 5, 4, 1, 6]) """ items = list(self) items.sort(key=key, reverse=reverse) return type(self)(items=items)
[docs] def split(self, weights, cyclic=False, overhang=False) -> "Sequence": r""" Splits sequence by ``weights``. .. container:: example Splits sequence cyclically by weights with overhang: .. container:: example >>> sequence = abjad.Sequence([10, -10, 10, -10]) >>> for part in sequence.split( ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ): ... part ... Sequence([3]) Sequence([7, -8]) Sequence([-2, 1]) Sequence([3]) Sequence([6, -9]) Sequence([-1]) .. container:: example Splits sequence once by weights with overhang: >>> for part in sequence.split( ... (3, 15, 3), ... cyclic=False, ... overhang=True, ... ): ... part ... Sequence([3]) Sequence([7, -8]) Sequence([-2, 1]) Sequence([9, -10]) .. container:: example Splits sequence once by weights without overhang: >>> for part in sequence.split( ... (3, 15, 3), ... cyclic=False, ... overhang=False, ... ): ... part ... Sequence([3]) Sequence([7, -8]) Sequence([-2, 1]) .. container:: example REGRESSION. Splits sequence of nonreduced fractions cyclically by weights with overhang: .. container:: example >>> sequence = abjad.Sequence([ ... abjad.NonreducedFraction(20, 2), ... abjad.NonreducedFraction(-20, 2), ... abjad.NonreducedFraction(20, 2), ... abjad.NonreducedFraction(-20, 2), ... ]) >>> for part in sequence.split( ... (3, 15, 3), ... cyclic=True, ... overhang=True, ... ): ... part ... Sequence([NonreducedFraction(6, 2)]) Sequence([NonreducedFraction(14, 2), NonreducedFraction(-16, 2)]) Sequence([NonreducedFraction(-4, 2), NonreducedFraction(2, 2)]) Sequence([NonreducedFraction(6, 2)]) Sequence([NonreducedFraction(12, 2), NonreducedFraction(-18, 2)]) Sequence([NonreducedFraction(-2, 2)]) """ result = [] current_index = 0 current_piece: typing.List[typing.Any] = [] if cyclic: weights = Sequence(weights).repeat_to_weight( _math.weight(self), allow_total=enums.Less ) for weight in weights: current_piece_weight = _math.weight(current_piece) while current_piece_weight < weight: current_piece.append(self[current_index]) current_index += 1 current_piece_weight = _math.weight(current_piece) if current_piece_weight == weight: current_piece_ = type(self)(current_piece) result.append(current_piece_) current_piece = [] elif weight < current_piece_weight: overage = current_piece_weight - weight current_last_element = current_piece.pop(-1) needed = abs(current_last_element) - overage needed *= _math.sign(current_last_element) current_piece.append(needed) current_piece_ = type(self)(current_piece) result.append(current_piece_) overage *= _math.sign(current_last_element) current_piece = [overage] if overhang: last_piece = current_piece last_piece.extend(self[current_index:]) if last_piece: last_piece_ = type(self)(last_piece) result.append(last_piece_) return type(self)(items=result)
[docs] def sum(self) -> typing.Any: r""" Sums sequence. .. container:: example Sums sequence of positive numbers: .. container:: example >>> sequence = abjad.Sequence([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) >>> sequence.sum() 55 .. container:: example Sum sequence of numbers with mixed signs: .. container:: example >>> sequence = abjad.Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) >>> sequence.sum() 5 .. container:: example Sums sequence and wraps result in new sequence: .. container:: example >>> sequence = abjad.Sequence(range(1, 10+1)) >>> result = sequence.sum() >>> sequence = abjad.Sequence(result) >>> sequence Sequence([55]) """ if len(self) == 0: return 0 result = self[0] for item in self[1:]: result += item return result
[docs] def sum_by_sign(self, sign=(-1, 0, 1)) -> "Sequence": """ Sums consecutive sequence items by ``sign``. >>> items = [0, 0, -1, -1, 2, 3, -5, 1, 2, 5, -5, -6] >>> sequence = abjad.Sequence(items) .. container:: example >>> sequence.sum_by_sign() Sequence([0, -2, 5, -5, 8, -11]) .. container:: example >>> sequence.sum_by_sign(sign=[-1]) Sequence([0, 0, -2, 2, 3, -5, 1, 2, 5, -11]) .. container:: example >>> sequence.sum_by_sign(sign=[0]) Sequence([0, -1, -1, 2, 3, -5, 1, 2, 5, -5, -6]) .. container:: example >>> sequence.sum_by_sign(sign=[1]) Sequence([0, 0, -1, -1, 5, -5, 8, -5, -6]) .. container:: example >>> sequence.sum_by_sign(sign=[-1, 0]) Sequence([0, -2, 2, 3, -5, 1, 2, 5, -11]) .. container:: example >>> sequence.sum_by_sign(sign=[-1, 1]) Sequence([0, 0, -2, 5, -5, 8, -11]) .. container:: example >>> sequence.sum_by_sign(sign=[0, 1]) Sequence([0, -1, -1, 5, -5, 8, -5, -6]) .. container:: example >>> sequence.sum_by_sign(sign=[-1, 0, 1]) Sequence([0, -2, 5, -5, 8, -11]) Sumsn consecutive negative elements when ``-1`` in ``sign``. Sums consecutive zero-valued elements when ``0`` in ``sign``. Sums consecutive positive elements when ``1`` in ``sign``. """ items = [] generator = itertools.groupby(self, _math.sign) for current_sign, group in generator: if current_sign in sign: items.append(sum(group)) else: for item in group: items.append(item) return type(self)(items=items)
[docs] def truncate(self, sum_=None, weight=None) -> "Sequence": """ Truncates sequence. >>> sequence = abjad.Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) .. container:: example Truncates sequence to weights ranging from 1 to 10: >>> for weight in range(1, 11): ... result = sequence.truncate(weight=weight) ... print(weight, result) ... 1 Sequence([-1]) 2 Sequence([-1, 1]) 3 Sequence([-1, 2]) 4 Sequence([-1, 2, -1]) 5 Sequence([-1, 2, -2]) 6 Sequence([-1, 2, -3]) 7 Sequence([-1, 2, -3, 1]) 8 Sequence([-1, 2, -3, 2]) 9 Sequence([-1, 2, -3, 3]) 10 Sequence([-1, 2, -3, 4]) .. container:: example Truncates sequence to sums ranging from 1 to 10: >>> for sum_ in range(1, 11): ... result = sequence.truncate(sum_=sum_) ... print(sum_, result) ... 1 Sequence([-1, 2]) 2 Sequence([-1, 2, -3, 4]) 3 Sequence([-1, 2, -3, 4, -5, 6]) 4 Sequence([-1, 2, -3, 4, -5, 6, -7, 8]) 5 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 6 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 7 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 8 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 9 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) 10 Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) .. container:: example Truncates sequence to zero weight: >>> sequence.truncate(weight=0) Sequence([]) .. container:: example Truncates sequence to zero sum: >>> sequence.truncate(sum_=0) Sequence([]) Ignores ``sum`` when ``weight`` and ``sum`` are both set. Raises value error on negative ``sum``. """ if weight is not None: assert 0 <= weight, repr(weight) items = [] if 0 < weight: total = 0 for item in self: total += abs(item) if total < weight: items.append(item) else: sign = _math.sign(item) trimmed_part = weight - _math.weight(items) trimmed_part *= sign items.append(trimmed_part) break elif sum_ is not None: assert 0 <= sum_, repr(sum_) items = [] if 0 < sum_: total = 0 for item in self: total += item if total < sum_: items.append(item) else: items.append(sum_ - sum(items)) break return type(self)(items=items)
[docs] def weight(self) -> typing.Any: """ Gets weight. .. container:: example >>> abjad.Sequence([]).weight() 0 >>> abjad.Sequence([1]).weight() 1 >>> abjad.Sequence([1, 2, 3]).weight() 6 >>> abjad.Sequence([1, 2, -3]).weight() 6 >>> abjad.Sequence([-1, -2, -3]).weight() 6 >>> sequence = abjad.Sequence([-1, 2, -3, 4, -5, 6, -7, 8, -9, 10]) >>> sequence.weight() 55 .. container:: example >>> abjad.Sequence([[1, -7, -7], [1, -8 -8]]).weight() 32 """ weights = [] for item in self: if hasattr(item, "weight"): weights.append(item.weight()) elif isinstance(item, collections.abc.Iterable): item = Sequence(item) weights.append(item.weight()) else: weights.append(abs(item)) return sum(weights)
[docs] def zip(self, cyclic=False, truncate=True) -> "Sequence": """ Zips sequences in sequence. .. container:: example Zips cyclically: >>> sequence = abjad.Sequence([[1, 2, 3], ['a', 'b']]) >>> for item in sequence.zip(cyclic=True): ... item ... Sequence([1, 'a']) Sequence([2, 'b']) Sequence([3, 'a']) >>> items = [[10, 11, 12], [20, 21], [30, 31, 32, 33]] >>> sequence = abjad.Sequence(items) >>> for item in sequence.zip(cyclic=True): ... item ... Sequence([10, 20, 30]) Sequence([11, 21, 31]) Sequence([12, 20, 32]) Sequence([10, 21, 33]) .. container:: example Zips without truncation: >>> items = [[1, 2, 3, 4], [11, 12, 13], [21, 22, 23]] >>> sequence = abjad.Sequence(items) >>> for item in sequence.zip(truncate=False): ... item ... Sequence([1, 11, 21]) Sequence([2, 12, 22]) Sequence([3, 13, 23]) Sequence([4]) .. container:: example Zips strictly: >>> items = [[1, 2, 3, 4], [11, 12, 13], [21, 22, 23]] >>> for item in abjad.Sequence(items).zip(): ... item ... Sequence([1, 11, 21]) Sequence([2, 12, 22]) Sequence([3, 13, 23]) Returns nested sequence. """ for item in self: if not isinstance(item, collections.abc.Iterable): raise Exception(f"must by iterable: {item!r}.") items: typing.List[typing.Any] = [] if cyclic: if not min(len(_) for _ in self): return type(self)(items=items) maximum_length = max([len(_) for _ in self]) for i in range(maximum_length): part = [] for item in self: index = i % len(item) element = item[index] part.append(element) part_ = type(self)(items=part) items.append(part_) elif not truncate: maximum_length = max([len(_) for _ in self]) for i in range(maximum_length): part = [] for item in self: try: part.append(item[i]) except IndexError: pass part_ = type(self)(items=part) items.append(part_) elif truncate: for item in zip(*self): item = type(self)(items=item) items.append(item) return type(self)(items=items)